From be32f6335f36889ac562bcf714cc2afc8c807b3e Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Fri, 29 Sep 2023 11:04:31 +0900
Subject: [PATCH 01/13] =?UTF-8?q?feat:=20PostService=E3=81=AE=E4=BE=9D?=
 =?UTF-8?q?=E5=AD=98=E9=96=A2=E4=BF=82=E3=82=92=E8=A6=8B=E7=9B=B4=E3=81=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../hideout/service/ap/APNoteService.kt       | 17 ++++++++++---
 .../hideout/service/post/PostService.kt       |  6 +++++
 .../hideout/service/post/PostServiceImpl.kt   | 24 ++++++++++++++-----
 3 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt b/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt
index 3c719104..c417a861 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt
@@ -17,6 +17,8 @@ import dev.usbharu.hideout.query.PostQueryService
 import dev.usbharu.hideout.query.UserQueryService
 import dev.usbharu.hideout.repository.PostRepository
 import dev.usbharu.hideout.service.job.JobQueueParentService
+import dev.usbharu.hideout.service.post.PostCreateInterceptor
+import dev.usbharu.hideout.service.post.PostService
 import io.ktor.client.*
 import io.ktor.client.statement.*
 import kjob.core.job.JobProps
@@ -45,9 +47,14 @@ class APNoteServiceImpl(
     private val followerQueryService: FollowerQueryService,
     private val postQueryService: PostQueryService,
     @Qualifier("activitypub") private val objectMapper: ObjectMapper,
-    private val applicationConfig: ApplicationConfig
+    private val applicationConfig: ApplicationConfig,
+    private val postService: PostService
 
-) : APNoteService {
+) : APNoteService, PostCreateInterceptor {
+
+    init {
+        postService.addInterceptor(this)
+    }
 
     private val logger = LoggerFactory.getLogger(this::class.java)
 
@@ -161,7 +168,7 @@ class APNoteServiceImpl(
             postQueryService.findByUrl(it)
         }
 
-        postRepository.save(
+        postService.createRemote(
             Post.of(
                 id = postRepository.generateId(),
                 userId = person.second.id,
@@ -185,4 +192,8 @@ class APNoteServiceImpl(
     companion object {
         const val public: String = "https://www.w3.org/ns/activitystreams#Public"
     }
+
+    override suspend fun run(post: Post) {
+        createNote(post)
+    }
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/PostService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/PostService.kt
index 0eed9d72..641ac4cd 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/PostService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/PostService.kt
@@ -7,4 +7,10 @@ import org.springframework.stereotype.Service
 @Service
 interface PostService {
     suspend fun createLocal(post: PostCreateDto): Post
+    suspend fun createRemote(post: Post): Post
+    fun addInterceptor(postCreateInterceptor: PostCreateInterceptor)
+}
+
+interface PostCreateInterceptor {
+    suspend fun run(post: Post)
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
index 0ba6b536..2c7cc38a 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
@@ -5,17 +5,32 @@ import dev.usbharu.hideout.domain.model.hideout.entity.Post
 import dev.usbharu.hideout.exception.UserNotFoundException
 import dev.usbharu.hideout.repository.PostRepository
 import dev.usbharu.hideout.repository.UserRepository
-import dev.usbharu.hideout.service.ap.APNoteService
 import org.springframework.stereotype.Service
 import java.time.Instant
+import java.util.*
 
 @Service
 class PostServiceImpl(
     private val postRepository: PostRepository,
     private val userRepository: UserRepository,
-    private val apNoteService: APNoteService
 ) : PostService {
+    private val interceptors = Collections.synchronizedList(mutableListOf<PostCreateInterceptor>())
+
     override suspend fun createLocal(post: PostCreateDto): Post {
+        val create = internalCreate(post)
+        interceptors.forEach { it.run(create) }
+        return create
+    }
+
+    override suspend fun createRemote(post: Post): Post {
+        return postRepository.save(post)
+    }
+
+    override fun addInterceptor(postCreateInterceptor: PostCreateInterceptor) {
+        interceptors.add(postCreateInterceptor)
+    }
+
+    private suspend fun internalCreate(post: PostCreateDto): Post {
         val user = userRepository.findById(post.userId) ?: throw UserNotFoundException("${post.userId} was not found")
         val id = postRepository.generateId()
         val createPost = Post.of(
@@ -29,9 +44,6 @@ class PostServiceImpl(
             repostId = null,
             replyId = null
         )
-        apNoteService.createNote(createPost)
-        return internalCreate(createPost)
+        return postRepository.save(createPost)
     }
-
-    private suspend fun internalCreate(post: Post): Post = postRepository.save(post)
 }

From f4716966c7dd0f98f140fc0d7976c7c595558a5c Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Fri, 29 Sep 2023 15:18:29 +0900
Subject: [PATCH 02/13] =?UTF-8?q?feat:=20Timeline=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

---
 build.gradle.kts                               |  1 +
 .../domain/model/hideout/entity/Timeline.kt    | 18 ++++++++++++++++++
 .../repository/MongoTimelineRepository.kt      |  9 +++++++++
 .../hideout/repository/TimelineRepository.kt   |  8 ++++++++
 .../hideout/service/post/PostServiceImpl.kt    |  9 +++++++--
 src/main/resources/application.yml             |  3 +++
 6 files changed, 46 insertions(+), 2 deletions(-)
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/repository/TimelineRepository.kt

diff --git a/build.gradle.kts b/build.gradle.kts
index ae1ed371..17396d06 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -110,6 +110,7 @@ dependencies {
     implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
     implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
     implementation("org.springframework.security:spring-security-oauth2-jose")
+    implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
 
     implementation("io.ktor:ktor-client-logging-jvm:$ktor_version")
 
diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt
new file mode 100644
index 00000000..a7536851
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt
@@ -0,0 +1,18 @@
+package dev.usbharu.hideout.domain.model.hideout.entity
+
+import org.springframework.data.annotation.Id
+import java.time.Instant
+
+data class Timeline(
+    @Id
+    val id: Long,
+    val userId: Long,
+    val timelineId: Long,
+    val postId: Long,
+    val postUserId: Long,
+    val createdAt: Instant,
+    val replyId: Long,
+    val repostId: Long,
+    val visibility: Visibility,
+    val sensitive: Boolean
+)
diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
new file mode 100644
index 00000000..7077fe99
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
@@ -0,0 +1,9 @@
+package dev.usbharu.hideout.repository
+
+import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
+import org.springframework.data.mongodb.repository.MongoRepository
+
+interface MongoTimelineRepository : TimelineRepository, MongoRepository<Timeline, Long> {
+    override fun findByUserId(id: Long): List<Timeline>
+    override fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/TimelineRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/TimelineRepository.kt
new file mode 100644
index 00000000..80b88278
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/TimelineRepository.kt
@@ -0,0 +1,8 @@
+package dev.usbharu.hideout.repository
+
+import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
+
+interface TimelineRepository {
+    fun findByUserId(id: Long): List<Timeline>
+    fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
index 2c7cc38a..d0f91b25 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
@@ -23,13 +23,18 @@ class PostServiceImpl(
     }
 
     override suspend fun createRemote(post: Post): Post {
-        return postRepository.save(post)
+        return internalCreate(post)
     }
 
     override fun addInterceptor(postCreateInterceptor: PostCreateInterceptor) {
         interceptors.add(postCreateInterceptor)
     }
 
+    private suspend fun internalCreate(post: Post): Post {
+
+        return postRepository.save(post)
+    }
+
     private suspend fun internalCreate(post: PostCreateDto): Post {
         val user = userRepository.findById(post.userId) ?: throw UserNotFoundException("${post.userId} was not found")
         val id = postRepository.generateId()
@@ -44,6 +49,6 @@ class PostServiceImpl(
             repostId = null,
             replyId = null
         )
-        return postRepository.save(createPost)
+        return internalCreate(post)
     }
 }
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 4d052250..876dbe3d 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -22,6 +22,9 @@ spring:
     url: "jdbc:h2:./test-dev2;MODE=POSTGRESQL"
     username: ""
     password: ""
+  data:
+    mongodb:
+
 
   h2:
     console:

From 948590c78fbaaa38ce5103035429c7852758fbe7 Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Fri, 29 Sep 2023 15:44:58 +0900
Subject: [PATCH 03/13] =?UTF-8?q?feat:=20TimelineService=E3=82=92=E4=BD=9C?=
 =?UTF-8?q?=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../domain/model/hideout/entity/Timeline.kt   |  7 ++--
 .../repository/MongoTimelineRepository.kt     |  8 ++--
 .../MongoTimelineRepositoryWrapper.kt         | 38 +++++++++++++++++++
 .../hideout/repository/TimelineRepository.kt  |  7 +++-
 .../hideout/service/post/PostServiceImpl.kt   |  5 ++-
 .../hideout/service/post/TimelineService.kt   | 31 +++++++++++++++
 src/main/resources/application.yml            |  6 ++-
 7 files changed, 90 insertions(+), 12 deletions(-)
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt

diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt
index a7536851..e8e591ea 100644
--- a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt
@@ -1,7 +1,6 @@
 package dev.usbharu.hideout.domain.model.hideout.entity
 
 import org.springframework.data.annotation.Id
-import java.time.Instant
 
 data class Timeline(
     @Id
@@ -10,9 +9,9 @@ data class Timeline(
     val timelineId: Long,
     val postId: Long,
     val postUserId: Long,
-    val createdAt: Instant,
-    val replyId: Long,
-    val repostId: Long,
+    val createdAt: Long,
+    val replyId: Long?,
+    val repostId: Long?,
     val visibility: Visibility,
     val sensitive: Boolean
 )
diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
index 7077fe99..716609f5 100644
--- a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
@@ -3,7 +3,9 @@ package dev.usbharu.hideout.repository
 import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
 import org.springframework.data.mongodb.repository.MongoRepository
 
-interface MongoTimelineRepository : TimelineRepository, MongoRepository<Timeline, Long> {
-    override fun findByUserId(id: Long): List<Timeline>
-    override fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
+interface MongoTimelineRepository : MongoRepository<Timeline, Long> {
+
+
+    fun findByUserId(id: Long): List<Timeline>
+    fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt
new file mode 100644
index 00000000..98674637
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt
@@ -0,0 +1,38 @@
+package dev.usbharu.hideout.repository
+
+import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
+import dev.usbharu.hideout.service.core.IdGenerateService
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.springframework.stereotype.Repository
+
+@Repository
+class MongoTimelineRepositoryWrapper(
+    private val mongoTimelineRepository: MongoTimelineRepository,
+    private val idGenerateService: IdGenerateService
+) :
+    TimelineRepository {
+    override suspend fun generateId(): Long = idGenerateService.generateId()
+
+    override suspend fun save(timeline: Timeline): Timeline {
+        return withContext(Dispatchers.IO) {
+            mongoTimelineRepository.save(timeline)
+        }
+    }
+
+    override suspend fun saveAll(timelines: List<Timeline>): List<Timeline> {
+        return mongoTimelineRepository.saveAll(timelines)
+    }
+
+    override suspend fun findByUserId(id: Long): List<Timeline> {
+        return withContext(Dispatchers.IO) {
+            mongoTimelineRepository.findByUserId(id)
+        }
+    }
+
+    override suspend fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline> {
+        return withContext(Dispatchers.IO) {
+            mongoTimelineRepository.findByUserIdAndTimelineId(userId, timelineId)
+        }
+    }
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/TimelineRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/TimelineRepository.kt
index 80b88278..76e9755c 100644
--- a/src/main/kotlin/dev/usbharu/hideout/repository/TimelineRepository.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/TimelineRepository.kt
@@ -3,6 +3,9 @@ package dev.usbharu.hideout.repository
 import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
 
 interface TimelineRepository {
-    fun findByUserId(id: Long): List<Timeline>
-    fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
+    suspend fun generateId(): Long
+    suspend fun save(timeline: Timeline): Timeline
+    suspend fun saveAll(timelines: List<Timeline>): List<Timeline>
+    suspend fun findByUserId(id: Long): List<Timeline>
+    suspend fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
index d0f91b25..f2531442 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
@@ -13,6 +13,7 @@ import java.util.*
 class PostServiceImpl(
     private val postRepository: PostRepository,
     private val userRepository: UserRepository,
+    private val timelineService: TimelineService
 ) : PostService {
     private val interceptors = Collections.synchronizedList(mutableListOf<PostCreateInterceptor>())
 
@@ -31,7 +32,7 @@ class PostServiceImpl(
     }
 
     private suspend fun internalCreate(post: Post): Post {
-
+        timelineService.publishTimeline(post)
         return postRepository.save(post)
     }
 
@@ -49,6 +50,6 @@ class PostServiceImpl(
             repostId = null,
             replyId = null
         )
-        return internalCreate(post)
+        return internalCreate(createPost)
     }
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
new file mode 100644
index 00000000..681893f7
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
@@ -0,0 +1,31 @@
+package dev.usbharu.hideout.service.post
+
+import dev.usbharu.hideout.domain.model.hideout.entity.Post
+import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
+import dev.usbharu.hideout.query.FollowerQueryService
+import dev.usbharu.hideout.repository.TimelineRepository
+import org.springframework.stereotype.Service
+
+@Service
+class TimelineService(
+    private val followerQueryService: FollowerQueryService,
+    private val timelineRepository: TimelineRepository
+) {
+    suspend fun publishTimeline(post: Post) {
+        val findFollowersById = followerQueryService.findFollowersById(post.userId)
+        timelineRepository.saveAll(findFollowersById.map {
+            Timeline(
+                id = timelineRepository.generateId(),
+                userId = it.id,
+                timelineId = 0,
+                postId = post.id,
+                postUserId = post.userId,
+                createdAt = post.createdAt,
+                replyId = post.replyId,
+                repostId = post.repostId,
+                visibility = post.visibility,
+                sensitive = post.sensitive
+            )
+        })
+    }
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 876dbe3d..889d71f4 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -24,7 +24,11 @@ spring:
     password: ""
   data:
     mongodb:
-
+      host: localhost
+      port: 27017
+      database: hideout
+  #      username: hideoutuser
+  #      password: hideoutpass
 
   h2:
     console:

From 4268f90fa5b8e205059950d9e48c999544e223c0 Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Fri, 29 Sep 2023 17:49:34 +0900
Subject: [PATCH 04/13] =?UTF-8?q?feat:=20GenerateTimelineService=E3=82=92?=
 =?UTF-8?q?=E4=BD=9C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../mastodon/MastodonTimelineApiController.kt |  30 ++++++
 .../domain/model/hideout/entity/Timeline.kt   |   3 +-
 .../query/mastodon/StatusQueryService.kt      |   7 ++
 .../query/mastodon/StatusQueryServiceImpl.kt  | 102 ++++++++++++++++++
 .../repository/MongoTimelineRepository.kt     |  11 +-
 .../service/post/GenerateTimelineService.kt   |  18 ++++
 .../post/MongoGenerateTimelineService.kt      |  32 ++++++
 .../hideout/service/post/PostServiceImpl.kt   |  12 +--
 .../hideout/service/post/TimelineService.kt   |   5 +-
 src/main/resources/openapi/mastodon.yaml      |  90 ++++++++++++++++
 10 files changed, 299 insertions(+), 11 deletions(-)
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonTimelineApiController.kt
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryService.kt
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/post/GenerateTimelineService.kt
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt

diff --git a/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonTimelineApiController.kt b/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonTimelineApiController.kt
new file mode 100644
index 00000000..03f3ace8
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonTimelineApiController.kt
@@ -0,0 +1,30 @@
+package dev.usbharu.hideout.controller.mastodon
+
+import dev.usbharu.hideout.controller.mastodon.generated.TimelineApi
+import dev.usbharu.hideout.domain.mastodon.model.generated.Status
+import org.springframework.http.ResponseEntity
+import org.springframework.stereotype.Controller
+
+@Controller
+class MastodonTimelineApiController : TimelineApi {
+    override fun apiV1TimelinesHomeGet(
+        maxId: String?,
+        sinceId: String?,
+        minId: String?,
+        limit: Int?
+    ): ResponseEntity<List<Status>> {
+
+    }
+
+    override fun apiV1TimelinesPublicGet(
+        local: Boolean?,
+        remote: Boolean?,
+        onlyMedia: Boolean?,
+        maxId: String?,
+        sinceId: String?,
+        minId: String?,
+        limit: Int?
+    ): ResponseEntity<List<Status>> {
+
+    }
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt
index e8e591ea..64f450a8 100644
--- a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Timeline.kt
@@ -13,5 +13,6 @@ data class Timeline(
     val replyId: Long?,
     val repostId: Long?,
     val visibility: Visibility,
-    val sensitive: Boolean
+    val sensitive: Boolean,
+    val isLocal: Boolean
 )
diff --git a/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryService.kt
new file mode 100644
index 00000000..a4ed048c
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryService.kt
@@ -0,0 +1,7 @@
+package dev.usbharu.hideout.query.mastodon
+
+import dev.usbharu.hideout.domain.mastodon.model.generated.Status
+
+interface StatusQueryService {
+    suspend fun findByPostIds(ids: List<Long>): List<Status>
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt
new file mode 100644
index 00000000..fb0cf012
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt
@@ -0,0 +1,102 @@
+package dev.usbharu.hideout.query.mastodon
+
+import dev.usbharu.hideout.domain.mastodon.model.generated.Account
+import dev.usbharu.hideout.domain.mastodon.model.generated.Status
+import dev.usbharu.hideout.repository.Posts
+import dev.usbharu.hideout.repository.Users
+import org.jetbrains.exposed.sql.innerJoin
+import org.jetbrains.exposed.sql.select
+import java.time.Instant
+
+class StatusQueryServiceImpl : StatusQueryService {
+    override suspend fun findByPostIds(ids: List<Long>): List<Status> {
+
+        val pairs = Posts.innerJoin(Users, onColumn = { userId }, otherColumn = { id })
+            .select { Posts.id inList ids }
+            .map {
+                Status(
+                    id = it[Posts.id].toString(),
+                    uri = it[Posts.apId],
+                    createdAt = Instant.ofEpochMilli(it[Posts.createdAt]).toString(),
+                    account = Account(
+                        id = it[Users.id].toString(),
+                        username = it[Users.name],
+                        acct = "${it[Users.name]}@${it[Users.domain]}",
+                        url = it[Users.url],
+                        displayName = it[Users.screenName],
+                        note = it[Users.description],
+                        avatar = it[Users.url] + "/icon.jpg",
+                        avatarStatic = it[Users.url] + "/icon.jpg",
+                        header = it[Users.url] + "/header.jpg",
+                        headerStatic = it[Users.url] + "/header.jpg",
+                        locked = false,
+                        fields = emptyList(),
+                        emojis = emptyList(),
+                        bot = false,
+                        group = false,
+                        discoverable = true,
+                        createdAt = Instant.ofEpochMilli(it[Users.createdAt]).toString(),
+                        lastStatusAt = Instant.ofEpochMilli(it[Users.createdAt]).toString(),
+                        statusesCount = 0,
+                        followersCount = 0,
+                        followingCount = 0,
+                        noindex = false,
+                        moved = false,
+                        suspendex = false,
+                        limited = false
+                    ),
+                    content = it[Posts.text],
+                    visibility = when (it[Posts.visibility]) {
+                        0 -> Status.Visibility.public
+                        1 -> Status.Visibility.unlisted
+                        2 -> Status.Visibility.private
+                        3 -> Status.Visibility.direct
+                        else -> Status.Visibility.public
+                    },
+                    sensitive = it[Posts.sensitive],
+                    spoilerText = it[Posts.overview].orEmpty(),
+                    mediaAttachments = emptyList(),
+                    mentions = emptyList(),
+                    tags = emptyList(),
+                    emojis = emptyList(),
+                    reblogsCount = 0,
+                    favouritesCount = 0,
+                    repliesCount = 0,
+                    url = it[Posts.apId],
+                    inReplyToId = it[Posts.replyId].toString(),
+                    inReplyToAccountId = null,
+                    language = null,
+                    text = it[Posts.text],
+                    editedAt = null,
+                    application = null,
+                    poll = null,
+                    card = null,
+                    favourited = null,
+                    reblogged = null,
+                    muted = null,
+                    bookmarked = null,
+                    pinned = null,
+                    filtered = null
+                ) to it[Posts.repostId]
+            }
+
+        val statuses = pairs.map { it.first }
+        return pairs
+            .map {
+                if (it.second != null) {
+                    it.first.copy(reblog = statuses.find { status -> status.id == it.second.toString() })
+                } else {
+                    it.first
+                }
+            }
+            .map {
+                if (it.inReplyToId != null) {
+                    it.copy(inReplyToAccountId = statuses.find { status -> status.id == it.inReplyToId }?.id)
+                } else {
+                    it
+                }
+            }
+
+
+    }
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
index 716609f5..2e92c7f4 100644
--- a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
@@ -1,11 +1,18 @@
 package dev.usbharu.hideout.repository
 
 import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
+import org.springframework.data.domain.Pageable
 import org.springframework.data.mongodb.repository.MongoRepository
 
 interface MongoTimelineRepository : MongoRepository<Timeline, Long> {
-
-
     fun findByUserId(id: Long): List<Timeline>
     fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
+    fun findByUserIdAndTimelineIdAndPostIdBetweenAndLocal(
+        userId: Long?,
+        timelineId: Long?,
+        postIdMin: Long?,
+        postIdMax: Long?,
+        isLocal: Boolean?,
+        pageable: Pageable
+    ): List<Timeline>
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/GenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/GenerateTimelineService.kt
new file mode 100644
index 00000000..ea1a26a4
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/GenerateTimelineService.kt
@@ -0,0 +1,18 @@
+package dev.usbharu.hideout.service.post
+
+import dev.usbharu.hideout.domain.mastodon.model.generated.Status
+import org.springframework.stereotype.Service
+
+@Service
+interface GenerateTimelineService {
+    suspend fun getTimeline(
+        forUserId: Long? = null,
+        localOnly: Boolean = false,
+        mediaOnly: Boolean = false,
+        maxId: Long? = null,
+        minId: Long? = null,
+        sinceId: Long? = null,
+        limit: Int = 20
+    ): List<Status>
+
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
new file mode 100644
index 00000000..8f1b7b33
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
@@ -0,0 +1,32 @@
+package dev.usbharu.hideout.service.post
+
+import dev.usbharu.hideout.domain.mastodon.model.generated.Status
+import dev.usbharu.hideout.query.mastodon.StatusQueryService
+import dev.usbharu.hideout.repository.MongoTimelineRepository
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.springframework.data.domain.Pageable
+
+class MongoGenerateTimelineService(
+    private val mongoTimelineRepository: MongoTimelineRepository,
+    private val statusQueryService: StatusQueryService
+) :
+    GenerateTimelineService {
+    override suspend fun getTimeline(
+        forUserId: Long?,
+        localOnly: Boolean,
+        mediaOnly: Boolean,
+        maxId: Long?,
+        minId: Long?,
+        sinceId: Long?,
+        limit: Int
+    ): List<Status> {
+        val timelines =
+            withContext(Dispatchers.IO) {
+                mongoTimelineRepository.findByUserIdAndTimelineIdAndPostIdBetweenAndLocal(
+                    forUserId, 0, maxId, minId, localOnly, Pageable.ofSize(limit)
+                )
+            }
+        return statusQueryService.findByPostIds(timelines.flatMap { setOfNotNull(it.postId, it.replyId, it.repostId) })
+    }
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
index f2531442..2fb9a1e4 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
@@ -18,25 +18,25 @@ class PostServiceImpl(
     private val interceptors = Collections.synchronizedList(mutableListOf<PostCreateInterceptor>())
 
     override suspend fun createLocal(post: PostCreateDto): Post {
-        val create = internalCreate(post)
+        val create = internalCreate(post, true)
         interceptors.forEach { it.run(create) }
         return create
     }
 
     override suspend fun createRemote(post: Post): Post {
-        return internalCreate(post)
+        return internalCreate(post, false)
     }
 
     override fun addInterceptor(postCreateInterceptor: PostCreateInterceptor) {
         interceptors.add(postCreateInterceptor)
     }
 
-    private suspend fun internalCreate(post: Post): Post {
-        timelineService.publishTimeline(post)
+    private suspend fun internalCreate(post: Post, isLocal: Boolean): Post {
+        timelineService.publishTimeline(post, isLocal)
         return postRepository.save(post)
     }
 
-    private suspend fun internalCreate(post: PostCreateDto): Post {
+    private suspend fun internalCreate(post: PostCreateDto, isLocal: Boolean): Post {
         val user = userRepository.findById(post.userId) ?: throw UserNotFoundException("${post.userId} was not found")
         val id = postRepository.generateId()
         val createPost = Post.of(
@@ -50,6 +50,6 @@ class PostServiceImpl(
             repostId = null,
             replyId = null
         )
-        return internalCreate(createPost)
+        return internalCreate(createPost, isLocal)
     }
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
index 681893f7..d0aeefbb 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
@@ -11,7 +11,7 @@ class TimelineService(
     private val followerQueryService: FollowerQueryService,
     private val timelineRepository: TimelineRepository
 ) {
-    suspend fun publishTimeline(post: Post) {
+    suspend fun publishTimeline(post: Post, isLocal: Boolean) {
         val findFollowersById = followerQueryService.findFollowersById(post.userId)
         timelineRepository.saveAll(findFollowersById.map {
             Timeline(
@@ -24,7 +24,8 @@ class TimelineService(
                 replyId = post.replyId,
                 repostId = post.repostId,
                 visibility = post.visibility,
-                sensitive = post.sensitive
+                sensitive = post.sensitive,
+                isLocal = isLocal
             )
         })
     }
diff --git a/src/main/resources/openapi/mastodon.yaml b/src/main/resources/openapi/mastodon.yaml
index 94b34dd6..5464a28a 100644
--- a/src/main/resources/openapi/mastodon.yaml
+++ b/src/main/resources/openapi/mastodon.yaml
@@ -15,6 +15,8 @@ tags:
     description: app
   - name: instance
     description: instance
+  - name: timeline
+    description: timeline
 
 paths:
   /api/v2/instance:
@@ -202,6 +204,94 @@ paths:
         200:
           description: 成功
 
+  /api/v1/timelines/public:
+    get:
+      tags:
+        - timeline
+      parameters:
+        - in: query
+          name: local
+          required: false
+          schema:
+            type: boolean
+        - in: query
+          name: remote
+          required: false
+          schema:
+            type: boolean
+        - in: query
+          name: only_media
+          required: false
+          schema:
+            type: boolean
+        - in: query
+          name: max_id
+          required: false
+          schema:
+            type: string
+        - in: query
+          name: since_id
+          required: false
+          schema:
+            type: string
+        - in: query
+          name: min_id
+          required: false
+          schema:
+            type: string
+        - in: query
+          name: limit
+          required: false
+          schema:
+            type: integer
+      responses:
+        200:
+          description: 成功
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: "#/components/schemas/Status"
+
+  /api/v1/timelines/home:
+    get:
+      tags:
+        - timeline
+      security:
+        - OAuth2:
+            - "read:statuses"
+      parameters:
+        - in: query
+          name: max_id
+          required: false
+          schema:
+            type: string
+        - in: query
+          name: since_id
+          required: false
+          schema:
+            type: string
+        - in: query
+          name: min_id
+          required: false
+          schema:
+            type: string
+        - in: query
+          name: limit
+          required: false
+          schema:
+            type: integer
+      responses:
+        200:
+          description: 成功
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: "#/components/schemas/Status"
+
 components:
   schemas:
     AccountsCreateRequest:

From 14022e9301e35ad027ad638bad1874dce32e0663 Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Fri, 29 Sep 2023 18:14:42 +0900
Subject: [PATCH 05/13] =?UTF-8?q?feat:=20Timeline=E3=81=AE=E3=82=A8?=
 =?UTF-8?q?=E3=83=B3=E3=83=89=E3=83=9D=E3=82=A4=E3=83=B3=E3=83=88=E3=82=92?=
 =?UTF-8?q?=E4=BD=9C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../mastodon/MastodonTimelineApiController.kt | 32 +++++++--
 .../query/mastodon/StatusQueryServiceImpl.kt  |  2 +
 .../repository/MongoTimelineRepository.kt     |  2 +-
 .../api/mastodon/TimelineApiService.kt        | 71 +++++++++++++++++++
 .../post/MongoGenerateTimelineService.kt      |  4 +-
 5 files changed, 104 insertions(+), 7 deletions(-)
 create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt

diff --git a/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonTimelineApiController.kt b/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonTimelineApiController.kt
index 03f3ace8..5d7b1bdb 100644
--- a/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonTimelineApiController.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonTimelineApiController.kt
@@ -2,18 +2,31 @@ package dev.usbharu.hideout.controller.mastodon
 
 import dev.usbharu.hideout.controller.mastodon.generated.TimelineApi
 import dev.usbharu.hideout.domain.mastodon.model.generated.Status
+import dev.usbharu.hideout.service.api.mastodon.TimelineApiService
+import kotlinx.coroutines.runBlocking
+import org.springframework.http.HttpStatus
 import org.springframework.http.ResponseEntity
+import org.springframework.security.core.context.SecurityContextHolder
+import org.springframework.security.oauth2.jwt.Jwt
 import org.springframework.stereotype.Controller
 
 @Controller
-class MastodonTimelineApiController : TimelineApi {
+class MastodonTimelineApiController(private val timelineApiService: TimelineApiService) : TimelineApi {
     override fun apiV1TimelinesHomeGet(
         maxId: String?,
         sinceId: String?,
         minId: String?,
         limit: Int?
-    ): ResponseEntity<List<Status>> {
-
+    ): ResponseEntity<List<Status>> = runBlocking {
+        val jwt = SecurityContextHolder.getContext().authentication.principal as Jwt
+        val homeTimeline = timelineApiService.homeTimeline(
+            userId = jwt.getClaim<String>("uid").toLong(),
+            maxId = maxId?.toLongOrNull(),
+            minId = minId?.toLongOrNull(),
+            sinceId = sinceId?.toLongOrNull(),
+            limit = limit ?: 20
+        )
+        ResponseEntity(homeTimeline, HttpStatus.OK)
     }
 
     override fun apiV1TimelinesPublicGet(
@@ -24,7 +37,16 @@ class MastodonTimelineApiController : TimelineApi {
         sinceId: String?,
         minId: String?,
         limit: Int?
-    ): ResponseEntity<List<Status>> {
-
+    ): ResponseEntity<List<Status>> = runBlocking {
+        val publicTimeline = timelineApiService.publicTimeline(
+            localOnly = local ?: false,
+            remoteOnly = remote ?: false,
+            mediaOnly = onlyMedia ?: false,
+            maxId = maxId?.toLongOrNull(),
+            minId = minId?.toLongOrNull(),
+            sinceId = sinceId?.toLongOrNull(),
+            limit = limit ?: 20
+        )
+        ResponseEntity(publicTimeline, HttpStatus.OK)
     }
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt
index fb0cf012..09d0c4a5 100644
--- a/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt
@@ -6,8 +6,10 @@ import dev.usbharu.hideout.repository.Posts
 import dev.usbharu.hideout.repository.Users
 import org.jetbrains.exposed.sql.innerJoin
 import org.jetbrains.exposed.sql.select
+import org.springframework.stereotype.Repository
 import java.time.Instant
 
+@Repository
 class StatusQueryServiceImpl : StatusQueryService {
     override suspend fun findByPostIds(ids: List<Long>): List<Status> {
 
diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
index 2e92c7f4..96a69f36 100644
--- a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
@@ -7,7 +7,7 @@ import org.springframework.data.mongodb.repository.MongoRepository
 interface MongoTimelineRepository : MongoRepository<Timeline, Long> {
     fun findByUserId(id: Long): List<Timeline>
     fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
-    fun findByUserIdAndTimelineIdAndPostIdBetweenAndLocal(
+    fun findByUserIdAndTimelineIdAndPostIdBetweenAndIsLocal(
         userId: Long?,
         timelineId: Long?,
         postIdMin: Long?,
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt b/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt
new file mode 100644
index 00000000..f27dcd8a
--- /dev/null
+++ b/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt
@@ -0,0 +1,71 @@
+package dev.usbharu.hideout.service.api.mastodon
+
+import dev.usbharu.hideout.domain.mastodon.model.generated.Status
+import dev.usbharu.hideout.service.core.Transaction
+import dev.usbharu.hideout.service.post.GenerateTimelineService
+import org.springframework.stereotype.Service
+
+interface TimelineApiService {
+    suspend fun publicTimeline(
+        localOnly: Boolean = false,
+        remoteOnly: Boolean = false,
+        mediaOnly: Boolean = false,
+        maxId: Long?,
+        minId: Long?,
+        sinceId: Long?,
+        limit: Int = 20
+    ): List<Status>
+
+    suspend fun homeTimeline(
+        userId: Long,
+        maxId: Long?,
+        minId: Long?,
+        sinceId: Long?,
+        limit: Int = 20
+    ): List<Status>
+}
+
+
+@Service
+class TimelineApiServiceImpl(
+    private val generateTimelineService: GenerateTimelineService,
+    private val transaction: Transaction
+) : TimelineApiService {
+    override suspend fun publicTimeline(
+        localOnly: Boolean,
+        remoteOnly: Boolean,
+        mediaOnly: Boolean,
+        maxId: Long?,
+        minId: Long?,
+        sinceId: Long?,
+        limit: Int
+    ): List<Status> = transaction.transaction {
+        generateTimelineService.getTimeline(
+            forUserId = null,
+            localOnly = localOnly,
+            mediaOnly = mediaOnly,
+            maxId = maxId,
+            minId = minId,
+            sinceId = sinceId,
+            limit = limit
+        )
+    }
+
+    override suspend fun homeTimeline(
+        userId: Long,
+        maxId: Long?,
+        minId: Long?,
+        sinceId: Long?,
+        limit: Int
+    ): List<Status> = transaction.transaction {
+        generateTimelineService.getTimeline(
+            forUserId = userId,
+            localOnly = false,
+            mediaOnly = false,
+            maxId = maxId,
+            minId = minId,
+            sinceId = sinceId,
+            limit = limit
+        )
+    }
+}
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
index 8f1b7b33..80ecf75e 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
@@ -6,7 +6,9 @@ import dev.usbharu.hideout.repository.MongoTimelineRepository
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import org.springframework.data.domain.Pageable
+import org.springframework.stereotype.Service
 
+@Service
 class MongoGenerateTimelineService(
     private val mongoTimelineRepository: MongoTimelineRepository,
     private val statusQueryService: StatusQueryService
@@ -23,7 +25,7 @@ class MongoGenerateTimelineService(
     ): List<Status> {
         val timelines =
             withContext(Dispatchers.IO) {
-                mongoTimelineRepository.findByUserIdAndTimelineIdAndPostIdBetweenAndLocal(
+                mongoTimelineRepository.findByUserIdAndTimelineIdAndPostIdBetweenAndIsLocal(
                     forUserId, 0, maxId, minId, localOnly, Pageable.ofSize(limit)
                 )
             }

From cab50e61a5a382c5f05381f6caef735794623f0d Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Fri, 29 Sep 2023 19:19:05 +0900
Subject: [PATCH 06/13] =?UTF-8?q?feat:=20=E5=8B=95=E7=9A=84=E3=81=AB?=
 =?UTF-8?q?=E3=82=AF=E3=82=A8=E3=83=AA=E3=82=92=E6=A7=8B=E7=AF=89=E3=81=99?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../post/MongoGenerateTimelineService.kt      | 38 ++++++++++++++-----
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
index 80ecf75e..d2399c3b 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
@@ -1,17 +1,19 @@
 package dev.usbharu.hideout.service.post
 
 import dev.usbharu.hideout.domain.mastodon.model.generated.Status
+import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
 import dev.usbharu.hideout.query.mastodon.StatusQueryService
 import dev.usbharu.hideout.repository.MongoTimelineRepository
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
-import org.springframework.data.domain.Pageable
+import org.springframework.data.mongodb.core.MongoTemplate
+import org.springframework.data.mongodb.core.query.Criteria
+import org.springframework.data.mongodb.core.query.Query
 import org.springframework.stereotype.Service
 
 @Service
 class MongoGenerateTimelineService(
     private val mongoTimelineRepository: MongoTimelineRepository,
-    private val statusQueryService: StatusQueryService
+    private val statusQueryService: StatusQueryService,
+    private val mongoTemplate: MongoTemplate
 ) :
     GenerateTimelineService {
     override suspend fun getTimeline(
@@ -23,12 +25,28 @@ class MongoGenerateTimelineService(
         sinceId: Long?,
         limit: Int
     ): List<Status> {
-        val timelines =
-            withContext(Dispatchers.IO) {
-                mongoTimelineRepository.findByUserIdAndTimelineIdAndPostIdBetweenAndIsLocal(
-                    forUserId, 0, maxId, minId, localOnly, Pageable.ofSize(limit)
-                )
-            }
+
+
+        val query = Query()
+        if (forUserId != null) {
+            val criteria = Criteria.where("userId").`is`(forUserId)
+            query.addCriteria(criteria)
+        }
+        if (localOnly) {
+            val criteria = Criteria.where("isLocal").`is`(true)
+            query.addCriteria(criteria)
+        }
+        if (maxId != null) {
+            val criteria = Criteria.where("postId").lt(maxId)
+            query.addCriteria(criteria)
+        }
+        if (minId != null) {
+            val criteria = Criteria.where("postId").gt(minId)
+            query.addCriteria(criteria)
+        }
+
+        val timelines = mongoTemplate.find(query.limit(limit), Timeline::class.java)
+
         return statusQueryService.findByPostIds(timelines.flatMap { setOfNotNull(it.postId, it.replyId, it.repostId) })
     }
 }

From 41f7fc605cdf7551551e5d8d745a6dd97546d25f Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Fri, 29 Sep 2023 19:19:30 +0900
Subject: [PATCH 07/13] =?UTF-8?q?feat:=20=E3=82=BF=E3=82=A4=E3=83=A0?=
 =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=AE=E6=A7=8B=E7=AF=89=E6=99=82?=
 =?UTF-8?q?=E3=81=AB=E8=87=AA=E5=88=86=E8=87=AA=E8=BA=AB=E3=82=92=E5=AF=BE?=
 =?UTF-8?q?=E8=B1=A1=E3=81=AB=E5=90=AB=E3=82=81=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../hideout/service/post/TimelineService.kt   | 29 +++++++++++++++++--
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
index d0aeefbb..944b1dda 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
@@ -2,18 +2,23 @@ package dev.usbharu.hideout.service.post
 
 import dev.usbharu.hideout.domain.model.hideout.entity.Post
 import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
+import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
 import dev.usbharu.hideout.query.FollowerQueryService
+import dev.usbharu.hideout.query.UserQueryService
 import dev.usbharu.hideout.repository.TimelineRepository
 import org.springframework.stereotype.Service
 
 @Service
 class TimelineService(
     private val followerQueryService: FollowerQueryService,
+    private val userQueryService: UserQueryService,
     private val timelineRepository: TimelineRepository
 ) {
     suspend fun publishTimeline(post: Post, isLocal: Boolean) {
-        val findFollowersById = followerQueryService.findFollowersById(post.userId)
-        timelineRepository.saveAll(findFollowersById.map {
+        // 自分自身も含める必要がある
+        val user = userQueryService.findById(post.userId)
+        val findFollowersById = followerQueryService.findFollowersById(post.userId).plus(user)
+        val timelines = findFollowersById.map {
             Timeline(
                 id = timelineRepository.generateId(),
                 userId = it.id,
@@ -27,6 +32,24 @@ class TimelineService(
                 sensitive = post.sensitive,
                 isLocal = isLocal
             )
-        })
+        }.toMutableList()
+        if (post.visibility == Visibility.PUBLIC) {
+            timelines.add(
+                Timeline(
+                    id = timelineRepository.generateId(),
+                    userId = 0,
+                    timelineId = 0,
+                    postId = post.id,
+                    postUserId = post.userId,
+                    createdAt = post.createdAt,
+                    replyId = post.replyId,
+                    repostId = post.repostId,
+                    visibility = post.visibility,
+                    sensitive = post.sensitive,
+                    isLocal = isLocal
+                )
+            )
+        }
+        timelineRepository.saveAll(timelines)
     }
 }

From af0d97f1a8dcf92e36d0a4d2be52005ed12baf1e Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Fri, 29 Sep 2023 19:20:57 +0900
Subject: [PATCH 08/13] =?UTF-8?q?fix:=20=E3=83=AA=E3=83=A2=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE=E5=A0=B4?=
 =?UTF-8?q?=E5=90=88=E3=81=AF=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4?=
 =?UTF-8?q?=E3=83=B3=E3=81=AB=E8=87=AA=E5=88=86=E8=87=AA=E8=BA=AB=E3=82=92?=
 =?UTF-8?q?=E5=90=AB=E3=82=81=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../dev/usbharu/hideout/service/post/TimelineService.kt  | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
index 944b1dda..97f83a08 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/TimelineService.kt
@@ -15,9 +15,12 @@ class TimelineService(
     private val timelineRepository: TimelineRepository
 ) {
     suspend fun publishTimeline(post: Post, isLocal: Boolean) {
-        // 自分自身も含める必要がある
-        val user = userQueryService.findById(post.userId)
-        val findFollowersById = followerQueryService.findFollowersById(post.userId).plus(user)
+        val findFollowersById = followerQueryService.findFollowersById(post.userId).toMutableList()
+        if (isLocal) {
+            // 自分自身も含める必要がある
+            val user = userQueryService.findById(post.userId)
+            findFollowersById.add(user)
+        }
         val timelines = findFollowersById.map {
             Timeline(
                 id = timelineRepository.generateId(),

From 6f9aa2a37801f717c0e13aacfb9297cf625dbd54 Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Sat, 30 Sep 2023 09:55:34 +0900
Subject: [PATCH 09/13] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?=
 =?UTF-8?q?=E4=BE=9D=E5=AD=98=E3=82=92=E5=89=8A=E9=99=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../hideout/service/post/MongoGenerateTimelineService.kt        | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
index d2399c3b..4c4a3808 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
@@ -3,7 +3,6 @@ package dev.usbharu.hideout.service.post
 import dev.usbharu.hideout.domain.mastodon.model.generated.Status
 import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
 import dev.usbharu.hideout.query.mastodon.StatusQueryService
-import dev.usbharu.hideout.repository.MongoTimelineRepository
 import org.springframework.data.mongodb.core.MongoTemplate
 import org.springframework.data.mongodb.core.query.Criteria
 import org.springframework.data.mongodb.core.query.Query
@@ -11,7 +10,6 @@ import org.springframework.stereotype.Service
 
 @Service
 class MongoGenerateTimelineService(
-    private val mongoTimelineRepository: MongoTimelineRepository,
     private val statusQueryService: StatusQueryService,
     private val mongoTemplate: MongoTemplate
 ) :

From cca71aa9413fc911950b5bdd5d356dbcc2e86967 Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Sat, 30 Sep 2023 10:38:23 +0900
Subject: [PATCH 10/13] =?UTF-8?q?feat:=20public=E3=82=BF=E3=82=A4=E3=83=A0?=
 =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=AF=E5=B0=82=E7=94=A8=E3=81=AE?=
 =?UTF-8?q?=E7=89=B9=E6=AE=8A=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=A7?=
 =?UTF-8?q?=E8=A6=8B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../usbharu/hideout/service/api/mastodon/TimelineApiService.kt  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt b/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt
index f27dcd8a..677b65ba 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt
@@ -41,7 +41,7 @@ class TimelineApiServiceImpl(
         limit: Int
     ): List<Status> = transaction.transaction {
         generateTimelineService.getTimeline(
-            forUserId = null,
+            forUserId = 0,
             localOnly = localOnly,
             mediaOnly = mediaOnly,
             maxId = maxId,

From 8297d578bcf7bb599c66bd30a37f13a947627db0 Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Sat, 30 Sep 2023 11:14:30 +0900
Subject: [PATCH 11/13] =?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

---
 .../service/ap/APNoteServiceImplTest.kt       | 20 ++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt
index 26e1ae4e..4c40d000 100644
--- a/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt
+++ b/src/test/kotlin/dev/usbharu/hideout/service/ap/APNoteServiceImplTest.kt
@@ -80,15 +80,16 @@ class APNoteServiceImplTest {
         val jobQueueParentService = mock<JobQueueParentService>()
         val activityPubNoteService =
             APNoteServiceImpl(
-                mock(),
-                jobQueueParentService,
-                mock(),
-                mock(),
-                userQueryService,
-                followerQueryService,
-                mock(),
+                httpClient = mock(),
+                jobQueueParentService = jobQueueParentService,
+                postRepository = mock(),
+                apUserService = mock(),
+                userQueryService = userQueryService,
+                followerQueryService = followerQueryService,
+                postQueryService = mock(),
                 objectMapper = objectMapper,
-                applicationConfig = testApplicationConfig
+                applicationConfig = testApplicationConfig,
+                postService = mock()
             )
         val postEntity = Post.of(
             1L,
@@ -121,7 +122,8 @@ class APNoteServiceImplTest {
             followerQueryService = mock(),
             postQueryService = mock(),
             objectMapper = objectMapper,
-            applicationConfig = testApplicationConfig
+            applicationConfig = testApplicationConfig,
+            postService = mock()
         )
         activityPubNoteService.createNoteJob(
             JobProps(

From 0d7e4aaedcbcf490417d501a109c642ca0b303cf Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Sat, 30 Sep 2023 11:19:06 +0900
Subject: [PATCH 12/13] =?UTF-8?q?style:=20=E3=82=B9=E3=82=BF=E3=82=A4?=
 =?UTF-8?q?=E3=83=AB=E3=82=92=E8=AA=BF=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../controller/mastodon/MastodonStatusesApiContoller.kt       | 2 +-
 .../usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt  | 4 +---
 .../dev/usbharu/hideout/repository/MongoTimelineRepository.kt | 1 +
 .../hideout/repository/MongoTimelineRepositoryWrapper.kt      | 1 +
 .../kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt    | 1 +
 .../hideout/service/api/mastodon/TimelineApiService.kt        | 2 +-
 .../usbharu/hideout/service/post/GenerateTimelineService.kt   | 2 +-
 .../hideout/service/post/MongoGenerateTimelineService.kt      | 2 --
 8 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonStatusesApiContoller.kt b/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonStatusesApiContoller.kt
index 0d40471e..d5111577 100644
--- a/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonStatusesApiContoller.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/controller/mastodon/MastodonStatusesApiContoller.kt
@@ -22,5 +22,5 @@ class MastodonStatusesApiContoller(private val statusesApiService: StatusesApiSe
                 statusesApiService.postStatus(statusesRequest, jwt.getClaim<String>("uid").toLong()),
                 HttpStatus.OK
             )
-    }
+        }
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt
index 09d0c4a5..71d3e0e1 100644
--- a/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/query/mastodon/StatusQueryServiceImpl.kt
@@ -11,8 +11,8 @@ import java.time.Instant
 
 @Repository
 class StatusQueryServiceImpl : StatusQueryService {
+    @Suppress("LongMethod")
     override suspend fun findByPostIds(ids: List<Long>): List<Status> {
-
         val pairs = Posts.innerJoin(Users, onColumn = { userId }, otherColumn = { id })
             .select { Posts.id inList ids }
             .map {
@@ -98,7 +98,5 @@ class StatusQueryServiceImpl : StatusQueryService {
                     it
                 }
             }
-
-
     }
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
index 96a69f36..3dcd35b1 100644
--- a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
@@ -4,6 +4,7 @@ import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
 import org.springframework.data.domain.Pageable
 import org.springframework.data.mongodb.repository.MongoRepository
 
+@Suppress("LongParameterList")
 interface MongoTimelineRepository : MongoRepository<Timeline, Long> {
     fun findByUserId(id: Long): List<Timeline>
     fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt
index 98674637..d4908948 100644
--- a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt
@@ -7,6 +7,7 @@ import kotlinx.coroutines.withContext
 import org.springframework.stereotype.Repository
 
 @Repository
+@Suppress("InjectDispatcher")
 class MongoTimelineRepositoryWrapper(
     private val mongoTimelineRepository: MongoTimelineRepository,
     private val idGenerateService: IdGenerateService
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt b/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt
index c417a861..b4c260e1 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt
@@ -38,6 +38,7 @@ interface APNoteService {
 }
 
 @Service
+@Suppress("LongParameterList")
 class APNoteServiceImpl(
     private val httpClient: HttpClient,
     private val jobQueueParentService: JobQueueParentService,
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt b/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt
index 677b65ba..2dfc1f24 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/api/mastodon/TimelineApiService.kt
@@ -5,6 +5,7 @@ import dev.usbharu.hideout.service.core.Transaction
 import dev.usbharu.hideout.service.post.GenerateTimelineService
 import org.springframework.stereotype.Service
 
+@Suppress("LongParameterList")
 interface TimelineApiService {
     suspend fun publicTimeline(
         localOnly: Boolean = false,
@@ -25,7 +26,6 @@ interface TimelineApiService {
     ): List<Status>
 }
 
-
 @Service
 class TimelineApiServiceImpl(
     private val generateTimelineService: GenerateTimelineService,
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/GenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/GenerateTimelineService.kt
index ea1a26a4..34f42678 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/GenerateTimelineService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/GenerateTimelineService.kt
@@ -4,6 +4,7 @@ import dev.usbharu.hideout.domain.mastodon.model.generated.Status
 import org.springframework.stereotype.Service
 
 @Service
+@Suppress("LongParameterList")
 interface GenerateTimelineService {
     suspend fun getTimeline(
         forUserId: Long? = null,
@@ -14,5 +15,4 @@ interface GenerateTimelineService {
         sinceId: Long? = null,
         limit: Int = 20
     ): List<Status>
-
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
index 4c4a3808..fa5c9800 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/MongoGenerateTimelineService.kt
@@ -23,8 +23,6 @@ class MongoGenerateTimelineService(
         sinceId: Long?,
         limit: Int
     ): List<Status> {
-
-
         val query = Query()
         if (forUserId != null) {
             val criteria = Criteria.where("userId").`is`(forUserId)

From 4ca9fe6cfbf45317b57895b365883364848b0b2a Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Sat, 30 Sep 2023 11:25:31 +0900
Subject: [PATCH 13/13] =?UTF-8?q?style:=20=E3=82=B9=E3=82=BF=E3=82=A4?=
 =?UTF-8?q?=E3=83=AB=E3=82=92=E8=AA=BF=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../usbharu/hideout/repository/MongoTimelineRepository.kt | 2 +-
 .../hideout/repository/MongoTimelineRepositoryWrapper.kt  | 5 ++---
 .../dev/usbharu/hideout/service/ap/APNoteService.kt       | 8 ++++----
 .../dev/usbharu/hideout/service/post/PostServiceImpl.kt   | 4 +---
 4 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
index 3dcd35b1..0b8937cf 100644
--- a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepository.kt
@@ -4,7 +4,7 @@ import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
 import org.springframework.data.domain.Pageable
 import org.springframework.data.mongodb.repository.MongoRepository
 
-@Suppress("LongParameterList")
+@Suppress("LongParameterList", "FunctionMaxLength")
 interface MongoTimelineRepository : MongoRepository<Timeline, Long> {
     fun findByUserId(id: Long): List<Timeline>
     fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
diff --git a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt
index d4908948..a3e31b6f 100644
--- a/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/repository/MongoTimelineRepositoryWrapper.kt
@@ -21,9 +21,8 @@ class MongoTimelineRepositoryWrapper(
         }
     }
 
-    override suspend fun saveAll(timelines: List<Timeline>): List<Timeline> {
-        return mongoTimelineRepository.saveAll(timelines)
-    }
+    override suspend fun saveAll(timelines: List<Timeline>): List<Timeline> =
+        mongoTimelineRepository.saveAll(timelines)
 
     override suspend fun findByUserId(id: Long): List<Timeline> {
         return withContext(Dispatchers.IO) {
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt b/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt
index b4c260e1..2b6ce229 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/ap/APNoteService.kt
@@ -190,11 +190,11 @@ class APNoteServiceImpl(
     override suspend fun fetchNote(note: Note, targetActor: String?): Note =
         note(note, targetActor, note.id ?: throw IllegalArgumentException("note.id is null"))
 
-    companion object {
-        const val public: String = "https://www.w3.org/ns/activitystreams#Public"
-    }
-
     override suspend fun run(post: Post) {
         createNote(post)
     }
+
+    companion object {
+        const val public: String = "https://www.w3.org/ns/activitystreams#Public"
+    }
 }
diff --git a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
index 2fb9a1e4..a86c2227 100644
--- a/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
+++ b/src/main/kotlin/dev/usbharu/hideout/service/post/PostServiceImpl.kt
@@ -23,9 +23,7 @@ class PostServiceImpl(
         return create
     }
 
-    override suspend fun createRemote(post: Post): Post {
-        return internalCreate(post, false)
-    }
+    override suspend fun createRemote(post: Post): Post = internalCreate(post, false)
 
     override fun addInterceptor(postCreateInterceptor: PostCreateInterceptor) {
         interceptors.add(postCreateInterceptor)