mirror of https://github.com/usbharu/Hideout.git
fix: メディア付き投稿に失敗する問題を修正
This commit is contained in:
parent
42b9d4e64b
commit
ced41e64fd
|
@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.stereotype.Component
|
||||
import java.net.URI
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.copyTo
|
||||
|
||||
|
@ -19,9 +20,16 @@ class LocalFileSystemMediaStore(
|
|||
MediaStore {
|
||||
|
||||
private val publicUrl = localStorageConfig.publicUrl ?: "${applicationConfig.url}/files/"
|
||||
|
||||
private val savePath = Path.of(localStorageConfig.path)
|
||||
|
||||
init {
|
||||
Files.createDirectories(savePath)
|
||||
}
|
||||
|
||||
override suspend fun upload(path: Path, id: String): URI {
|
||||
logger.info("START Media upload. {}", id)
|
||||
val fileSavePath = buildSavePath(path, id)
|
||||
val fileSavePath = buildSavePath(savePath, id)
|
||||
|
||||
val fileSavePathString = fileSavePath.toAbsolutePath().toString()
|
||||
logger.info("MEDIA save. path: {}", fileSavePathString)
|
||||
|
|
|
@ -97,6 +97,10 @@ tasks {
|
|||
|
||||
importMappings.put("org.springframework.core.io.Resource", "org.springframework.web.multipart.MultipartFile")
|
||||
typeMappings.put("org.springframework.core.io.Resource", "org.springframework.web.multipart.MultipartFile")
|
||||
schemaMappings.put(
|
||||
"StatusesRequest",
|
||||
"dev.usbharu.hideout.mastodon.interfaces.api.StatusesRequest"
|
||||
)
|
||||
templateDir.set("$rootDir/templates")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
package dev.usbharu.hideout.mastodon.application.status
|
||||
|
||||
import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService
|
||||
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
|
||||
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status
|
||||
import dev.usbharu.hideout.mastodon.query.StatusQueryService
|
||||
import org.slf4j.LoggerFactory
|
||||
|
@ -28,7 +28,7 @@ import org.springframework.stereotype.Service
|
|||
class GetStatusApplicationService(
|
||||
private val statusQueryService: StatusQueryService,
|
||||
transaction: Transaction,
|
||||
) : LocalUserAbstractApplicationService<GetStatus, Status>(
|
||||
) : AbstractApplicationService<GetStatus, Status>(
|
||||
transaction,
|
||||
logger
|
||||
) {
|
||||
|
@ -36,7 +36,7 @@ class GetStatusApplicationService(
|
|||
val logger = LoggerFactory.getLogger(GetStatusApplicationService::class.java)!!
|
||||
}
|
||||
|
||||
override suspend fun internalExecute(command: GetStatus, principal: FromApi): Status {
|
||||
override suspend fun internalExecute(command: GetStatus, principal: Principal): Status {
|
||||
return statusQueryService.findByPostId(command.id.toLong(), principal)
|
||||
?: throw IllegalArgumentException("Post ${command.id} not found.")
|
||||
|
||||
|
|
|
@ -79,11 +79,11 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
emojiIdSet.addAll(statusQueries.flatMap { it.emojiIds })
|
||||
|
||||
val qa = authorizedQuery()
|
||||
|
||||
val postMap = Posts
|
||||
val replyToAlias = Posts.alias("reply_to")
|
||||
val postMap = qa
|
||||
.leftJoin(Actors)
|
||||
.selectAll().where { Posts.id inList postIdSet }
|
||||
.associate { it[Posts.id] to toStatus(it, qa) }
|
||||
.associate { it[Posts.id] to toStatus(it, qa, replyToAlias) }
|
||||
val mediaMap = Media.selectAll().where { Media.id inList mediaIdSet }
|
||||
.associate {
|
||||
it[Media.id] to it.toMedia().toMediaAttachments()
|
||||
|
@ -112,6 +112,7 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
tagged: String?,
|
||||
includeFollowers: Boolean,
|
||||
): List<Status> {
|
||||
val inReplyToAlias = Posts.alias("reply_to")
|
||||
val qa = authorizedQuery()
|
||||
val query = qa
|
||||
.leftJoin(PostsMedia)
|
||||
|
@ -138,7 +139,7 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
.groupBy { it[Posts.id] }
|
||||
.map { it.value }
|
||||
.map {
|
||||
toStatus(it.first(), qa).copy(
|
||||
toStatus(it.first(), qa, inReplyToAlias).copy(
|
||||
mediaAttachments = it.mapNotNull { resultRow ->
|
||||
resultRow.toMediaOrNull()?.toMediaAttachments()
|
||||
}
|
||||
|
@ -151,16 +152,18 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
|
||||
override suspend fun findByPostId(id: Long, principal: Principal?): Status? {
|
||||
val aq = authorizedQuery(principal)
|
||||
val inReplyTo = Posts.alias("reply_to")
|
||||
val map = aq
|
||||
.leftJoin(PostsMedia, { aq[Posts.id] }, { PostsMedia.postId })
|
||||
.leftJoin(Actors, { aq[Posts.actorId] }, { Actors.id })
|
||||
.leftJoin(Media, { PostsMedia.mediaId }, { Media.id })
|
||||
.leftJoin(inReplyTo, { aq[Posts.replyId] }, { inReplyTo[Posts.id] })
|
||||
.selectAll()
|
||||
.where { aq[Posts.id] eq id }
|
||||
.groupBy { it[aq[Posts.id]] }
|
||||
.map { it.value }
|
||||
.map {
|
||||
toStatus(it.first(), aq).copy(
|
||||
toStatus(it.first(), aq, inReplyTo).copy(
|
||||
mediaAttachments = it.mapNotNull { resultRow ->
|
||||
resultRow.toMediaOrNull()?.toMediaAttachments()
|
||||
},
|
||||
|
@ -180,18 +183,10 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
it.first
|
||||
}
|
||||
}
|
||||
.map {
|
||||
if (it.inReplyToId != null) {
|
||||
println("statuses trace: $statuses")
|
||||
println("inReplyToId trace: ${it.inReplyToId}")
|
||||
it.copy(inReplyToAccountId = statuses.find { (id) -> id == it.inReplyToId }?.account?.id)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun findByPostIdsWithMedia(ids: List<Long>): List<Status> {
|
||||
val inReplyToAlias = Posts.alias("reply_to")
|
||||
val qa = authorizedQuery()
|
||||
val pairs = Posts
|
||||
.leftJoin(PostsMedia)
|
||||
|
@ -203,7 +198,7 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
.groupBy { it[Posts.id] }
|
||||
.map { it.value }
|
||||
.map {
|
||||
toStatus(it.first(), qa).copy(
|
||||
toStatus(it.first(), qa, inReplyToAlias).copy(
|
||||
mediaAttachments = it.mapNotNull { resultRow ->
|
||||
resultRow.toMediaOrNull()?.toMediaAttachments()
|
||||
},
|
||||
|
@ -222,7 +217,7 @@ private fun CustomEmoji.toMastodonEmoji(): MastodonEmoji = MastodonEmoji(
|
|||
category = this.category.orEmpty()
|
||||
)
|
||||
|
||||
private fun toStatus(it: ResultRow, queryAlias: QueryAlias) = Status(
|
||||
private fun toStatus(it: ResultRow, queryAlias: QueryAlias, inReplyToAlias: Alias<Posts>) = Status(
|
||||
id = it[queryAlias[Posts.id]].toString(),
|
||||
uri = it[queryAlias[Posts.apId]],
|
||||
createdAt = it[queryAlias[Posts.createdAt]].toString(),
|
||||
|
@ -271,7 +266,7 @@ private fun toStatus(it: ResultRow, queryAlias: QueryAlias) = Status(
|
|||
repliesCount = 0,
|
||||
url = it[queryAlias[Posts.apId]],
|
||||
inReplyToId = it[queryAlias[Posts.replyId]]?.toString(),
|
||||
inReplyToAccountId = null,
|
||||
inReplyToAccountId = it.getOrNull(inReplyToAlias[Posts.actorId])?.toString(),
|
||||
language = null,
|
||||
text = it[queryAlias[Posts.text]],
|
||||
editedAt = null
|
||||
|
@ -305,12 +300,12 @@ fun ResultRow.toMediaOrNull(): EntityMedia? {
|
|||
type = FileType.valueOf(this[Media.type]),
|
||||
blurHash = this[Media.blurhash]?.let { MediaBlurHash(it) },
|
||||
mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType),
|
||||
description = MediaDescription(this[Media.description] ?: return null)
|
||||
description = this[Media.description]?.let { MediaDescription(it) }
|
||||
)
|
||||
}
|
||||
|
||||
fun EntityMedia.toMediaAttachments(): MediaAttachment = MediaAttachment(
|
||||
id = id.toString(),
|
||||
id = id.id.toString(),
|
||||
type = when (type) {
|
||||
FileType.Image -> MediaAttachment.Type.image
|
||||
FileType.Video -> MediaAttachment.Type.video
|
||||
|
|
|
@ -22,10 +22,9 @@ import dev.usbharu.hideout.core.domain.model.post.Visibility
|
|||
import dev.usbharu.hideout.core.domain.model.support.principal.PrincipalContextHolder
|
||||
import dev.usbharu.hideout.mastodon.application.status.GetStatus
|
||||
import dev.usbharu.hideout.mastodon.application.status.GetStatusApplicationService
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.StatusesRequest.Visibility.*
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.StatusApi
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.StatusesRequest
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.StatusesRequest.Visibility.*
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.stereotype.Controller
|
||||
|
||||
|
@ -52,13 +51,13 @@ class SpringStatusApi(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun apiV1StatusesPost(statusesRequest: StatusesRequest): ResponseEntity<Status> {
|
||||
override suspend fun apiV1StatusesPost(statusesRequest: dev.usbharu.hideout.mastodon.interfaces.api.StatusesRequest): ResponseEntity<Status> {
|
||||
|
||||
val principal = principalContextHolder.getPrincipal()
|
||||
val execute = registerLocalPostApplicationService.execute(
|
||||
RegisterLocalPost(
|
||||
content = statusesRequest.status.orEmpty(),
|
||||
overview = statusesRequest.spoilerText,
|
||||
overview = statusesRequest.spoiler_text,
|
||||
visibility = when (statusesRequest.visibility) {
|
||||
public -> Visibility.PUBLIC
|
||||
unlisted -> Visibility.UNLISTED
|
||||
|
@ -67,9 +66,9 @@ class SpringStatusApi(
|
|||
null -> Visibility.PUBLIC
|
||||
},
|
||||
repostId = null,
|
||||
replyId = statusesRequest.inReplyToId?.toLong(),
|
||||
replyId = statusesRequest.in_reply_to_id?.toLong(),
|
||||
sensitive = statusesRequest.sensitive == true,
|
||||
mediaIds = statusesRequest.mediaIds.orEmpty().map { it.toLong() }
|
||||
mediaIds = statusesRequest.media_ids.map { it.toLong() }
|
||||
), principal
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
package dev.usbharu.hideout.mastodon.interfaces.api
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.StatusesRequest.Visibility.*
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.StatusesRequestPoll
|
||||
|
||||
@Suppress("VariableNaming", "EnumEntryName")
|
||||
class StatusesRequest {
|
||||
@JsonProperty("status")
|
||||
var status: String? = null
|
||||
|
||||
@JsonProperty("media_ids")
|
||||
var media_ids: List<String> = emptyList()
|
||||
|
||||
@JsonProperty("poll")
|
||||
var poll: StatusesRequestPoll? = null
|
||||
|
||||
@JsonProperty("in_reply_to_id")
|
||||
var in_reply_to_id: String? = null
|
||||
|
||||
@JsonProperty("sensitive")
|
||||
var sensitive: Boolean? = null
|
||||
|
||||
@JsonProperty("spoiler_text")
|
||||
var spoiler_text: String? = null
|
||||
|
||||
@JsonProperty("visibility")
|
||||
var visibility: Visibility? = null
|
||||
|
||||
@JsonProperty("language")
|
||||
var language: String? = null
|
||||
|
||||
@JsonProperty("scheduled_at")
|
||||
var scheduled_at: String? = null
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is StatusesRequest) return false
|
||||
|
||||
if (status != other.status) return false
|
||||
if (media_ids != other.media_ids) return false
|
||||
if (poll != other.poll) return false
|
||||
if (in_reply_to_id != other.in_reply_to_id) return false
|
||||
if (sensitive != other.sensitive) return false
|
||||
if (spoiler_text != other.spoiler_text) return false
|
||||
if (visibility != other.visibility) return false
|
||||
if (language != other.language) return false
|
||||
if (scheduled_at != other.scheduled_at) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = status?.hashCode() ?: 0
|
||||
result = 31 * result + media_ids.hashCode()
|
||||
result = 31 * result + (poll?.hashCode() ?: 0)
|
||||
result = 31 * result + (in_reply_to_id?.hashCode() ?: 0)
|
||||
result = 31 * result + (sensitive?.hashCode() ?: 0)
|
||||
result = 31 * result + (spoiler_text?.hashCode() ?: 0)
|
||||
result = 31 * result + (visibility?.hashCode() ?: 0)
|
||||
result = 31 * result + (language?.hashCode() ?: 0)
|
||||
result = 31 * result + (scheduled_at?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "StatusesRequest(status=$status, mediaIds=$media_ids, poll=$poll, inReplyToId=$in_reply_to_id, " +
|
||||
"sensitive=$sensitive, spoilerText=$spoiler_text, visibility=$visibility, language=$language," +
|
||||
" scheduledAt=$scheduled_at)"
|
||||
}
|
||||
|
||||
@Suppress("EnumNaming", "EnumEntryNameCase")
|
||||
enum class Visibility {
|
||||
`public`,
|
||||
unlisted,
|
||||
private,
|
||||
direct
|
||||
}
|
||||
}
|
||||
|
||||
fun StatusesRequest.Visibility?.toPostVisibility(): Visibility {
|
||||
return when (this) {
|
||||
public -> Visibility.PUBLIC
|
||||
unlisted -> Visibility.UNLISTED
|
||||
private -> Visibility.FOLLOWERS
|
||||
direct -> Visibility.DIRECT
|
||||
null -> Visibility.PUBLIC
|
||||
}
|
||||
}
|
||||
|
||||
fun StatusesRequest.Visibility?.toStatusVisibility(): Status.Visibility {
|
||||
return when (this) {
|
||||
public -> Status.Visibility.public
|
||||
unlisted -> Status.Visibility.unlisted
|
||||
private -> Status.Visibility.private
|
||||
direct -> Status.Visibility.direct
|
||||
null -> Status.Visibility.public
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue