mirror of https://github.com/usbharu/Hideout.git
commit
171a5ad26c
|
@ -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")
|
||||
|
||||
|
|
|
@ -22,5 +22,5 @@ class MastodonStatusesApiContoller(private val statusesApiService: StatusesApiSe
|
|||
statusesApiService.postStatus(statusesRequest, jwt.getClaim<String>("uid").toLong()),
|
||||
HttpStatus.OK
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
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(private val timelineApiService: TimelineApiService) : TimelineApi {
|
||||
override fun apiV1TimelinesHomeGet(
|
||||
maxId: String?,
|
||||
sinceId: String?,
|
||||
minId: String?,
|
||||
limit: Int?
|
||||
): 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(
|
||||
local: Boolean?,
|
||||
remote: Boolean?,
|
||||
onlyMedia: Boolean?,
|
||||
maxId: String?,
|
||||
sinceId: String?,
|
||||
minId: String?,
|
||||
limit: Int?
|
||||
): 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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package dev.usbharu.hideout.domain.model.hideout.entity
|
||||
|
||||
import org.springframework.data.annotation.Id
|
||||
|
||||
data class Timeline(
|
||||
@Id
|
||||
val id: Long,
|
||||
val userId: Long,
|
||||
val timelineId: Long,
|
||||
val postId: Long,
|
||||
val postUserId: Long,
|
||||
val createdAt: Long,
|
||||
val replyId: Long?,
|
||||
val repostId: Long?,
|
||||
val visibility: Visibility,
|
||||
val sensitive: Boolean,
|
||||
val isLocal: Boolean
|
||||
)
|
|
@ -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>
|
||||
}
|
|
@ -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 org.springframework.stereotype.Repository
|
||||
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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
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
|
||||
|
||||
@Suppress("LongParameterList", "FunctionMaxLength")
|
||||
interface MongoTimelineRepository : MongoRepository<Timeline, Long> {
|
||||
fun findByUserId(id: Long): List<Timeline>
|
||||
fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
|
||||
fun findByUserIdAndTimelineIdAndPostIdBetweenAndIsLocal(
|
||||
userId: Long?,
|
||||
timelineId: Long?,
|
||||
postIdMin: Long?,
|
||||
postIdMax: Long?,
|
||||
isLocal: Boolean?,
|
||||
pageable: Pageable
|
||||
): List<Timeline>
|
||||
}
|
|
@ -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
|
||||
@Suppress("InjectDispatcher")
|
||||
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> =
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package dev.usbharu.hideout.repository
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Timeline
|
||||
|
||||
interface TimelineRepository {
|
||||
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>
|
||||
}
|
|
@ -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
|
||||
|
@ -36,6 +38,7 @@ interface APNoteService {
|
|||
}
|
||||
|
||||
@Service
|
||||
@Suppress("LongParameterList")
|
||||
class APNoteServiceImpl(
|
||||
private val httpClient: HttpClient,
|
||||
private val jobQueueParentService: JobQueueParentService,
|
||||
|
@ -45,9 +48,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 +169,7 @@ class APNoteServiceImpl(
|
|||
postQueryService.findByUrl(it)
|
||||
}
|
||||
|
||||
postRepository.save(
|
||||
postService.createRemote(
|
||||
Post.of(
|
||||
id = postRepository.generateId(),
|
||||
userId = person.second.id,
|
||||
|
@ -182,6 +190,10 @@ class APNoteServiceImpl(
|
|||
override suspend fun fetchNote(note: Note, targetActor: String?): Note =
|
||||
note(note, targetActor, note.id ?: throw IllegalArgumentException("note.id is null"))
|
||||
|
||||
override suspend fun run(post: Post) {
|
||||
createNote(post)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val public: String = "https://www.w3.org/ns/activitystreams#Public"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
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 = 0,
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
@Suppress("LongParameterList")
|
||||
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>
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
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 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 statusQueryService: StatusQueryService,
|
||||
private val mongoTemplate: MongoTemplate
|
||||
) :
|
||||
GenerateTimelineService {
|
||||
override suspend fun getTimeline(
|
||||
forUserId: Long?,
|
||||
localOnly: Boolean,
|
||||
mediaOnly: Boolean,
|
||||
maxId: Long?,
|
||||
minId: Long?,
|
||||
sinceId: Long?,
|
||||
limit: Int
|
||||
): List<Status> {
|
||||
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) })
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -5,17 +5,36 @@ 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
|
||||
private val timelineService: TimelineService
|
||||
) : PostService {
|
||||
private val interceptors = Collections.synchronizedList(mutableListOf<PostCreateInterceptor>())
|
||||
|
||||
override suspend fun createLocal(post: PostCreateDto): Post {
|
||||
val create = internalCreate(post, true)
|
||||
interceptors.forEach { it.run(create) }
|
||||
return create
|
||||
}
|
||||
|
||||
override suspend fun createRemote(post: Post): Post = internalCreate(post, false)
|
||||
|
||||
override fun addInterceptor(postCreateInterceptor: PostCreateInterceptor) {
|
||||
interceptors.add(postCreateInterceptor)
|
||||
}
|
||||
|
||||
private suspend fun internalCreate(post: Post, isLocal: Boolean): Post {
|
||||
timelineService.publishTimeline(post, isLocal)
|
||||
return postRepository.save(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(
|
||||
|
@ -29,9 +48,6 @@ class PostServiceImpl(
|
|||
repostId = null,
|
||||
replyId = null
|
||||
)
|
||||
apNoteService.createNote(createPost)
|
||||
return internalCreate(createPost)
|
||||
return internalCreate(createPost, isLocal)
|
||||
}
|
||||
|
||||
private suspend fun internalCreate(post: Post): Post = postRepository.save(post)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
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).toMutableList()
|
||||
if (isLocal) {
|
||||
// 自分自身も含める必要がある
|
||||
val user = userQueryService.findById(post.userId)
|
||||
findFollowersById.add(user)
|
||||
}
|
||||
val timelines = 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,
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -22,6 +22,13 @@ spring:
|
|||
url: "jdbc:h2:./test-dev2;MODE=POSTGRESQL"
|
||||
username: ""
|
||||
password: ""
|
||||
data:
|
||||
mongodb:
|
||||
host: localhost
|
||||
port: 27017
|
||||
database: hideout
|
||||
# username: hideoutuser
|
||||
# password: hideoutpass
|
||||
|
||||
h2:
|
||||
console:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue