mirror of https://github.com/usbharu/Hideout.git
wip
This commit is contained in:
parent
e13858e068
commit
922bdb4991
|
@ -17,7 +17,7 @@
|
||||||
package activitypub.webfinger
|
package activitypub.webfinger
|
||||||
|
|
||||||
import dev.usbharu.hideout.SpringApplication
|
import dev.usbharu.hideout.SpringApplication
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
import dev.usbharu.owl.producer.api.OwlProducer
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.flywaydb.core.Flyway
|
import org.flywaydb.core.Flyway
|
||||||
|
|
|
@ -20,8 +20,6 @@ import dev.usbharu.hideout.SpringApplication
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
|
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.CustomEmojis
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.CustomEmojis
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction
|
|
||||||
import dev.usbharu.owl.producer.api.OwlProducer
|
import dev.usbharu.owl.producer.api.OwlProducer
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
|
|
||||||
object TestTransaction : Transaction {
|
object TestTransaction : Transaction {
|
||||||
override suspend fun <T> transaction(block: suspend () -> T): T = block()
|
override suspend fun <T> transaction(block: suspend () -> T): T = block()
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser
|
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser
|
||||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
import dev.usbharu.httpsignature.common.HttpHeaders
|
||||||
|
|
|
@ -25,7 +25,7 @@ import com.nimbusds.jose.jwk.source.JWKSource
|
||||||
import com.nimbusds.jose.proc.SecurityContext
|
import com.nimbusds.jose.proc.SecurityContext
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.StringORObjectSerializer
|
import dev.usbharu.hideout.activitypub.domain.model.StringORObjectSerializer
|
||||||
import dev.usbharu.hideout.activitypub.domain.model.StringOrObject
|
import dev.usbharu.hideout.activitypub.domain.model.StringOrObject
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter
|
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker
|
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker
|
||||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService
|
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService
|
||||||
|
|
|
@ -35,7 +35,6 @@ class OwlProducerRunner(private val owlProducer: OwlProducer, private val taskDe
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun destroy() {
|
override fun destroy() {
|
||||||
System.err.println("destroy aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
owlProducer.stop()
|
owlProducer.stop()
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.application.infrastructure.exposed
|
package dev.usbharu.hideout.application.infrastructure.exposed
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.slf4j.MDCContext
|
import kotlinx.coroutines.slf4j.MDCContext
|
||||||
import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger
|
import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.application.service.init
|
package dev.usbharu.hideout.application.service.init
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.meta.Jwt
|
|
||||||
import dev.usbharu.hideout.core.domain.model.meta.Meta
|
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
|
|
@ -16,11 +16,8 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.application.service.init
|
package dev.usbharu.hideout.application.service.init
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.exception.NotInitException
|
import dev.usbharu.hideout.core.domain.exception.NotInitException
|
||||||
import dev.usbharu.hideout.core.domain.model.meta.Jwt
|
|
||||||
import dev.usbharu.hideout.core.domain.model.meta.Meta
|
|
||||||
import dev.usbharu.hideout.core.domain.model.meta.MetaRepository
|
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.application.service.init
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
|
||||||
import dev.usbharu.hideout.core.domain.model.meta.Jwt
|
|
||||||
import dev.usbharu.hideout.core.domain.model.meta.Meta
|
|
||||||
import dev.usbharu.hideout.core.domain.model.meta.MetaRepository
|
|
||||||
import dev.usbharu.hideout.util.ServerUtil
|
|
||||||
import org.slf4j.Logger
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.security.KeyPairGenerator
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class ServerInitialiseServiceImpl(
|
|
||||||
private val metaRepository: MetaRepository,
|
|
||||||
private val transaction: Transaction
|
|
||||||
) :
|
|
||||||
ServerInitialiseService {
|
|
||||||
|
|
||||||
val logger: Logger = LoggerFactory.getLogger(ServerInitialiseServiceImpl::class.java)
|
|
||||||
|
|
||||||
override suspend fun init() {
|
|
||||||
transaction.transaction {
|
|
||||||
val savedMeta = metaRepository.get()
|
|
||||||
val implementationVersion = ServerUtil.getImplementationVersion()
|
|
||||||
if (wasInitialised(savedMeta).not()) {
|
|
||||||
logger.info("Start Initialise")
|
|
||||||
initialise(implementationVersion)
|
|
||||||
logger.info("Finish Initialise")
|
|
||||||
return@transaction
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isVersionChanged(requireNotNull(savedMeta))) {
|
|
||||||
logger.info("Version changed!! (${savedMeta.version} -> $implementationVersion)")
|
|
||||||
updateVersion(savedMeta, implementationVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun wasInitialised(meta: Meta?): Boolean {
|
|
||||||
logger.debug("Initialise checking...")
|
|
||||||
return meta != null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isVersionChanged(meta: Meta): Boolean = meta.version != ServerUtil.getImplementationVersion()
|
|
||||||
|
|
||||||
private suspend fun initialise(implementationVersion: String) {
|
|
||||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
|
||||||
keyPairGenerator.initialize(2048)
|
|
||||||
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
|
||||||
val jwt = Jwt(
|
|
||||||
UUID.randomUUID(),
|
|
||||||
Base64.getEncoder().encodeToString(generateKeyPair.private.encoded),
|
|
||||||
Base64.getEncoder().encodeToString(generateKeyPair.public.encoded)
|
|
||||||
)
|
|
||||||
val meta = Meta(implementationVersion, jwt)
|
|
||||||
metaRepository.save(meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun updateVersion(meta: Meta, version: String) {
|
|
||||||
metaRepository.save(meta.copy(version = version))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,9 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.actor
|
package dev.usbharu.hideout.core.application.actor
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
|
@ -14,9 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.actor
|
package dev.usbharu.hideout.core.application.actor
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||||
import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.*
|
import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.*
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.actor
|
package dev.usbharu.hideout.core.application.actor
|
||||||
|
|
||||||
data class RegisterLocalActor(
|
data class RegisterLocalActor(
|
||||||
val name: String,
|
val name: String,
|
|
@ -14,11 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.actor
|
package dev.usbharu.hideout.core.application.actor
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
|
||||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
||||||
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
|
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.usbharu.hideout.core.usecase.actor
|
package dev.usbharu.hideout.core.application.actor
|
||||||
|
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
|
|
@ -14,9 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.actor
|
package dev.usbharu.hideout.core.application.actor
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
|
@ -14,9 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.actor
|
package dev.usbharu.hideout.core.application.actor
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.post
|
package dev.usbharu.hideout.core.application.post
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post2Repository
|
import dev.usbharu.hideout.core.domain.model.post.Post2Repository
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.post
|
package dev.usbharu.hideout.core.application.post
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.post
|
package dev.usbharu.hideout.core.application.post
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.post
|
package dev.usbharu.hideout.core.application.post
|
||||||
|
|
||||||
data class UpdateLocalNote(
|
data class UpdateLocalNote(
|
||||||
val postId: Long,
|
val postId: Long,
|
|
@ -14,9 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.usecase.post
|
package dev.usbharu.hideout.core.application.post
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post2Repository
|
import dev.usbharu.hideout.core.domain.model.post.Post2Repository
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.application.external
|
package dev.usbharu.hideout.core.application.shared
|
||||||
|
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
|
|
@ -16,10 +16,13 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.emoji
|
package dev.usbharu.hideout.core.domain.model.emoji
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.shared.Domain
|
||||||
|
import java.net.URI
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
sealed class Emoji {
|
sealed class Emoji {
|
||||||
abstract val domain: String
|
abstract val domain: Domain
|
||||||
abstract val name: String
|
abstract val name: String
|
||||||
|
|
||||||
@Suppress("FunctionMinLength")
|
@Suppress("FunctionMinLength")
|
||||||
|
@ -33,13 +36,13 @@ sealed class Emoji {
|
||||||
}
|
}
|
||||||
|
|
||||||
data class CustomEmoji(
|
data class CustomEmoji(
|
||||||
val id: Long,
|
val id: EmojiId,
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val domain: String,
|
override val domain: Domain,
|
||||||
val instanceId: Long?,
|
val instanceId: InstanceId,
|
||||||
val url: String,
|
val url: URI,
|
||||||
val category: String?,
|
val category: String?,
|
||||||
val createdAt: Instant
|
val createdAt: Instant,
|
||||||
) : Emoji() {
|
) : Emoji() {
|
||||||
override fun id(): String = id.toString()
|
override fun id(): String = id.toString()
|
||||||
}
|
}
|
||||||
|
@ -47,6 +50,6 @@ data class CustomEmoji(
|
||||||
data class UnicodeEmoji(
|
data class UnicodeEmoji(
|
||||||
override val name: String
|
override val name: String
|
||||||
) : Emoji() {
|
) : Emoji() {
|
||||||
override val domain: String = "unicode.org"
|
override val domain: Domain = Domain("unicode.org")
|
||||||
override fun id(): String = name
|
override fun id(): String = name
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.filter
|
|
||||||
|
|
||||||
data class Filter(
|
|
||||||
val id: Long,
|
|
||||||
val userId: Long,
|
|
||||||
val name: String,
|
|
||||||
val context: List<FilterType>,
|
|
||||||
val filterAction: FilterAction,
|
|
||||||
)
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.filter
|
|
||||||
|
|
||||||
@Suppress("EnumEntryNameCase", "EnumNaming")
|
|
||||||
enum class FilterAction {
|
|
||||||
warn,
|
|
||||||
hide
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.filter
|
|
||||||
|
|
||||||
interface FilterRepository {
|
|
||||||
|
|
||||||
suspend fun generateId(): Long
|
|
||||||
suspend fun save(filter: Filter): Filter
|
|
||||||
suspend fun findById(id: Long): Filter?
|
|
||||||
|
|
||||||
suspend fun findByUserIdAndId(userId: Long, id: Long): Filter?
|
|
||||||
suspend fun findByUserIdAndType(userId: Long, types: List<FilterType>): List<Filter>
|
|
||||||
suspend fun deleteById(id: Long)
|
|
||||||
|
|
||||||
suspend fun deleteByUserIdAndId(userId: Long, id: Long)
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.filter
|
|
||||||
|
|
||||||
@Suppress("EnumEntryNameCase", "EnumNaming")
|
|
||||||
enum class FilterType {
|
|
||||||
home,
|
|
||||||
notifications,
|
|
||||||
public,
|
|
||||||
thread,
|
|
||||||
account
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.filterkeyword
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterMode
|
|
||||||
|
|
||||||
data class FilterKeyword(
|
|
||||||
val id: Long,
|
|
||||||
val filterId: Long,
|
|
||||||
val keyword: String,
|
|
||||||
val mode: FilterMode
|
|
||||||
)
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.filterkeyword
|
|
||||||
|
|
||||||
interface FilterKeywordRepository {
|
|
||||||
suspend fun generateId(): Long
|
|
||||||
suspend fun save(filterKeyword: FilterKeyword): FilterKeyword
|
|
||||||
suspend fun saveAll(filterKeywordList: List<FilterKeyword>)
|
|
||||||
suspend fun findById(id: Long): FilterKeyword?
|
|
||||||
suspend fun deleteById(id: Long)
|
|
||||||
suspend fun deleteByFilterId(filterId: Long)
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.instance
|
|
||||||
|
|
||||||
class Nodeinfo private constructor() {
|
|
||||||
|
|
||||||
var links: List<Links> = emptyList()
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as Nodeinfo
|
|
||||||
|
|
||||||
return links == other.links
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = links.hashCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Links private constructor() {
|
|
||||||
var rel: String? = null
|
|
||||||
var href: String? = null
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as Links
|
|
||||||
|
|
||||||
if (rel != other.rel) return false
|
|
||||||
if (href != other.href) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = rel?.hashCode() ?: 0
|
|
||||||
result = 31 * result + (href?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("Filename")
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.instance
|
|
||||||
|
|
||||||
@Suppress("ClassNaming")
|
|
||||||
class Nodeinfo2_0 {
|
|
||||||
var metadata: Metadata? = null
|
|
||||||
var software: Software? = null
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as Nodeinfo2_0
|
|
||||||
|
|
||||||
if (metadata != other.metadata) return false
|
|
||||||
if (software != other.software) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = metadata?.hashCode() ?: 0
|
|
||||||
result = 31 * result + (software?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Metadata {
|
|
||||||
var nodeName: String? = null
|
|
||||||
var nodeDescription: String? = null
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as Metadata
|
|
||||||
|
|
||||||
if (nodeName != other.nodeName) return false
|
|
||||||
if (nodeDescription != other.nodeDescription) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = nodeName?.hashCode() ?: 0
|
|
||||||
result = 31 * result + (nodeDescription?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Software {
|
|
||||||
var name: String? = null
|
|
||||||
var version: String? = null
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as Software
|
|
||||||
|
|
||||||
if (name != other.name) return false
|
|
||||||
if (version != other.version) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = name?.hashCode() ?: 0
|
|
||||||
result = 31 * result + (version?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
package dev.usbharu.hideout.core.domain.model.media
|
||||||
|
|
||||||
enum class FileType {
|
enum class FileType {
|
||||||
Image,
|
Image,
|
|
@ -16,34 +16,17 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.media
|
package dev.usbharu.hideout.core.domain.model.media
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.service.media.FileType
|
import java.net.URI
|
||||||
import dev.usbharu.hideout.core.service.media.MimeType
|
|
||||||
import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment
|
|
||||||
|
|
||||||
data class Media(
|
data class Media(
|
||||||
val id: Long,
|
val id: MediaId,
|
||||||
val name: String,
|
val name: MediaName,
|
||||||
val url: String,
|
val url: URI,
|
||||||
val remoteUrl: String?,
|
val remoteUrl: URI?,
|
||||||
val thumbnailUrl: String?,
|
val thumbnailUrl: URI?,
|
||||||
val type: FileType,
|
val type: FileType,
|
||||||
val mimeType: MimeType,
|
val mimeType: MimeType,
|
||||||
val blurHash: String?,
|
val blurHash: MediaBlurHash?,
|
||||||
val description: String? = null
|
val description: MediaDescription? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun Media.toMediaAttachments(): MediaAttachment = MediaAttachment(
|
|
||||||
id = id.toString(),
|
|
||||||
type = when (type) {
|
|
||||||
FileType.Image -> MediaAttachment.Type.image
|
|
||||||
FileType.Video -> MediaAttachment.Type.video
|
|
||||||
FileType.Audio -> MediaAttachment.Type.audio
|
|
||||||
FileType.Unknown -> MediaAttachment.Type.unknown
|
|
||||||
},
|
|
||||||
url = url,
|
|
||||||
previewUrl = thumbnailUrl,
|
|
||||||
remoteUrl = remoteUrl,
|
|
||||||
description = description,
|
|
||||||
blurhash = blurHash,
|
|
||||||
textUrl = url
|
|
||||||
)
|
|
||||||
|
|
|
@ -14,10 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.filter
|
package dev.usbharu.hideout.core.domain.model.media
|
||||||
|
|
||||||
enum class FilterMode {
|
@JvmInline
|
||||||
WHOLE_WORD,
|
value class MediaBlurHash(val hash: String)
|
||||||
REGEX,
|
|
||||||
NONE
|
|
||||||
}
|
|
|
@ -14,9 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.post
|
package dev.usbharu.hideout.core.domain.model.media
|
||||||
|
|
||||||
data class FormattedPostContent(
|
@JvmInline
|
||||||
val html: String,
|
value class MediaDescription(val description: String)
|
||||||
val content: String
|
|
||||||
)
|
|
|
@ -14,6 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.meta
|
package dev.usbharu.hideout.core.domain.model.media
|
||||||
|
|
||||||
data class Meta(val version: String, val jwt: Jwt)
|
@JvmInline
|
||||||
|
value class MediaName(val name: String)
|
|
@ -14,6 +14,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
package dev.usbharu.hideout.core.domain.model.media
|
||||||
|
|
||||||
data class MimeType(val type: String, val subtype: String, val fileType: FileType)
|
data class MimeType(val type: String, val subtype: String, val fileType: FileType)
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.meta
|
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
data class Jwt(val kid: UUID, val privateKey: String, val publicKey: String)
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.meta
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Repository
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
interface MetaRepository {
|
|
||||||
|
|
||||||
suspend fun save(meta: Meta)
|
|
||||||
|
|
||||||
suspend fun get(): Meta?
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.notification
|
|
||||||
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
data class Notification(
|
|
||||||
val id: Long,
|
|
||||||
val type: String,
|
|
||||||
val userId: Long,
|
|
||||||
val sourceActorId: Long?,
|
|
||||||
val postId: Long?,
|
|
||||||
val text: String?,
|
|
||||||
val reactionId: Long?,
|
|
||||||
val createdAt: Instant
|
|
||||||
)
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.notification
|
|
||||||
|
|
||||||
interface NotificationRepository {
|
|
||||||
suspend fun generateId(): Long
|
|
||||||
suspend fun save(notification: Notification): Notification
|
|
||||||
suspend fun findById(id: Long): Notification?
|
|
||||||
suspend fun deleteById(id: Long)
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.reaction
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.Emoji
|
|
||||||
|
|
||||||
data class Reaction(val id: Long, val emoji: Emoji, val postId: Long, val actorId: Long)
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.reaction
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.Emoji
|
|
||||||
import org.springframework.stereotype.Repository
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
@Suppress("FunctionMaxLength", "TooManyFunctions")
|
|
||||||
interface ReactionRepository {
|
|
||||||
suspend fun generateId(): Long
|
|
||||||
suspend fun save(reaction: Reaction): Reaction
|
|
||||||
suspend fun delete(reaction: Reaction): Reaction
|
|
||||||
suspend fun deleteByPostId(postId: Long): Int
|
|
||||||
suspend fun deleteByActorId(actorId: Long): Int
|
|
||||||
suspend fun deleteByPostIdAndActorId(postId: Long, actorId: Long)
|
|
||||||
suspend fun deleteByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji)
|
|
||||||
suspend fun findById(id: Long): Reaction?
|
|
||||||
suspend fun findByPostId(postId: Long): List<Reaction>
|
|
||||||
suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction?
|
|
||||||
suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean
|
|
||||||
suspend fun existByPostIdAndActorIdAndUnicodeEmoji(postId: Long, actorId: Long, unicodeEmoji: String): Boolean
|
|
||||||
suspend fun existByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji): Boolean
|
|
||||||
suspend fun existByPostIdAndActor(postId: Long, actorId: Long): Boolean
|
|
||||||
suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List<Reaction>
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.relationship
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ユーザーとの関係を表します
|
|
||||||
*
|
|
||||||
* @property actorId ユーザー
|
|
||||||
* @property targetActorId 相手ユーザー
|
|
||||||
* @property following フォローしているか
|
|
||||||
* @property blocking ブロックしているか
|
|
||||||
* @property muting ミュートしているか
|
|
||||||
* @property followRequest フォローリクエストを送っているか
|
|
||||||
* @property ignoreFollowRequestToTarget フォローリクエストを無視しているか
|
|
||||||
*/
|
|
||||||
data class Relationship(
|
|
||||||
val actorId: Long,
|
|
||||||
val targetActorId: Long,
|
|
||||||
val following: Boolean,
|
|
||||||
val blocking: Boolean,
|
|
||||||
val muting: Boolean,
|
|
||||||
val followRequest: Boolean,
|
|
||||||
val ignoreFollowRequestToTarget: Boolean
|
|
||||||
)
|
|
|
@ -1,178 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.relationship
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.infrastructure.exposed.Page
|
|
||||||
import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList
|
|
||||||
import dev.usbharu.hideout.application.infrastructure.exposed.withPagination
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository
|
|
||||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
|
||||||
import org.jetbrains.exposed.sql.*
|
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
|
||||||
import org.slf4j.Logger
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() {
|
|
||||||
override val logger: Logger
|
|
||||||
get() = Companion.logger
|
|
||||||
|
|
||||||
override suspend fun save(relationship: Relationship): Relationship = query {
|
|
||||||
val singleOrNull = Relationships.selectAll().where {
|
|
||||||
(Relationships.actorId eq relationship.actorId).and(
|
|
||||||
Relationships.targetActorId eq relationship.targetActorId
|
|
||||||
)
|
|
||||||
}.forUpdate().singleOrNull()
|
|
||||||
|
|
||||||
if (singleOrNull == null) {
|
|
||||||
Relationships.insert {
|
|
||||||
it[actorId] = relationship.actorId
|
|
||||||
it[targetActorId] = relationship.targetActorId
|
|
||||||
it[following] = relationship.following
|
|
||||||
it[blocking] = relationship.blocking
|
|
||||||
it[muting] = relationship.muting
|
|
||||||
it[followRequest] = relationship.followRequest
|
|
||||||
it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestToTarget
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Relationships.update({
|
|
||||||
(Relationships.actorId eq relationship.actorId).and(
|
|
||||||
Relationships.targetActorId eq relationship.targetActorId
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
it[following] = relationship.following
|
|
||||||
it[blocking] = relationship.blocking
|
|
||||||
it[muting] = relationship.muting
|
|
||||||
it[followRequest] = relationship.followRequest
|
|
||||||
it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestToTarget
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@query relationship
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun delete(relationship: Relationship): Unit = query {
|
|
||||||
Relationships.deleteWhere {
|
|
||||||
(actorId eq relationship.actorId).and(
|
|
||||||
targetActorId eq relationship.targetActorId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? = query {
|
|
||||||
return@query Relationships.selectAll()
|
|
||||||
.where { (Relationships.actorId eq actorId).and(Relationships.targetActorId eq targetActorId) }
|
|
||||||
.singleOrNull()?.toRelationships()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun deleteByActorIdOrTargetActorId(actorId: Long, targetActorId: Long): Unit = query {
|
|
||||||
Relationships.deleteWhere {
|
|
||||||
Relationships.actorId.eq(actorId).or(Relationships.targetActorId.eq(targetActorId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List<Relationship> = query {
|
|
||||||
return@query Relationships
|
|
||||||
.selectAll().where { Relationships.targetActorId eq targetId and (Relationships.following eq following) }
|
|
||||||
.map { it.toRelationships() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun countByTargetIdAndFollowing(targetId: Long, following: Boolean): Int = query {
|
|
||||||
return@query Relationships
|
|
||||||
.selectAll()
|
|
||||||
.where {
|
|
||||||
Relationships.targetActorId eq targetId and (Relationships.following eq following)
|
|
||||||
}
|
|
||||||
.count()
|
|
||||||
.toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun countByUserIdAndFollowing(userId: Long, following: Boolean): Int = query {
|
|
||||||
return@query Relationships
|
|
||||||
.selectAll()
|
|
||||||
.where {
|
|
||||||
Relationships.actorId eq userId and (Relationships.following eq following)
|
|
||||||
}
|
|
||||||
.count()
|
|
||||||
.toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest(
|
|
||||||
targetId: Long,
|
|
||||||
followRequest: Boolean,
|
|
||||||
ignoreFollowRequest: Boolean,
|
|
||||||
page: Page.PageByMaxId,
|
|
||||||
): PaginationList<Relationship, Long> = query {
|
|
||||||
val query = Relationships.selectAll().where {
|
|
||||||
Relationships.targetActorId.eq(targetId).and(Relationships.followRequest.eq(followRequest))
|
|
||||||
.and(Relationships.ignoreFollowRequestFromTarget.eq(ignoreFollowRequest))
|
|
||||||
}
|
|
||||||
|
|
||||||
val resultRowList = query.withPagination(page, Relationships.id)
|
|
||||||
|
|
||||||
return@query PaginationList(
|
|
||||||
resultRowList.map { it.toRelationships() },
|
|
||||||
resultRowList.next?.value,
|
|
||||||
resultRowList.prev?.value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByActorIdAndMuting(
|
|
||||||
actorId: Long,
|
|
||||||
muting: Boolean,
|
|
||||||
page: Page.PageByMaxId,
|
|
||||||
): PaginationList<Relationship, Long> = query {
|
|
||||||
val query =
|
|
||||||
Relationships.selectAll().where { Relationships.actorId.eq(actorId).and(Relationships.muting.eq(muting)) }
|
|
||||||
|
|
||||||
val resultRowList = query.withPagination(page, Relationships.id)
|
|
||||||
|
|
||||||
return@query PaginationList(
|
|
||||||
resultRowList.map { it.toRelationships() },
|
|
||||||
resultRowList.next?.value,
|
|
||||||
resultRowList.prev?.value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(RelationshipRepositoryImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ResultRow.toRelationships(): Relationship = Relationship(
|
|
||||||
actorId = this[Relationships.actorId],
|
|
||||||
targetActorId = this[Relationships.targetActorId],
|
|
||||||
following = this[Relationships.following],
|
|
||||||
blocking = this[Relationships.blocking],
|
|
||||||
muting = this[Relationships.muting],
|
|
||||||
followRequest = this[Relationships.followRequest],
|
|
||||||
ignoreFollowRequestToTarget = this[Relationships.ignoreFollowRequestFromTarget]
|
|
||||||
)
|
|
||||||
|
|
||||||
object Relationships : LongIdTable("relationships") {
|
|
||||||
val actorId = long("actor_id").references(Actors.id)
|
|
||||||
val targetActorId = long("target_actor_id").references(Actors.id)
|
|
||||||
val following = bool("following")
|
|
||||||
val blocking = bool("blocking")
|
|
||||||
val muting = bool("muting")
|
|
||||||
val followRequest = bool("follow_request")
|
|
||||||
val ignoreFollowRequestFromTarget = bool("ignore_follow_request")
|
|
||||||
|
|
||||||
init {
|
|
||||||
uniqueIndex(actorId, targetActorId)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.shared.domainevent
|
|
||||||
|
|
||||||
import java.time.Instant
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
data class DomainEvent(
|
|
||||||
private val id: String,
|
|
||||||
private val name: String,
|
|
||||||
private val occurredOn: Instant,
|
|
||||||
private val body: DomainEventBody,
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
fun create(name: String, body: DomainEventBody): DomainEvent {
|
|
||||||
return DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun reconstruct(id: String, name: String, occurredOn: Instant, body: DomainEventBody): DomainEvent {
|
|
||||||
return DomainEvent(id, name, occurredOn, body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.shared.domainevent
|
|
||||||
|
|
||||||
abstract class DomainEventBody(val map: Map<String, Any>) {
|
|
||||||
fun toMap(): Map<String, Any> {
|
|
||||||
return map
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.timeline
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
|
||||||
import org.springframework.data.annotation.Id
|
|
||||||
import org.springframework.data.mongodb.core.index.CompoundIndex
|
|
||||||
import org.springframework.data.mongodb.core.mapping.Document
|
|
||||||
|
|
||||||
@Document
|
|
||||||
@CompoundIndex(def = "{'userId':1,'timelineId':1,'postId':1}", unique = true)
|
|
||||||
data class Timeline(
|
|
||||||
@Id
|
|
||||||
val id: Long,
|
|
||||||
val userId: Long,
|
|
||||||
val timelineId: Long,
|
|
||||||
val postId: Long,
|
|
||||||
val postActorId: Long,
|
|
||||||
val createdAt: Long,
|
|
||||||
val replyId: Long?,
|
|
||||||
val repostId: Long?,
|
|
||||||
val visibility: Visibility,
|
|
||||||
val sensitive: Boolean,
|
|
||||||
val isLocal: Boolean,
|
|
||||||
val isPureRepost: Boolean = false,
|
|
||||||
val mediaIds: List<Long> = emptyList(),
|
|
||||||
val emojiIds: List<Long> = emptyList()
|
|
||||||
)
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.domain.model.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>
|
|
||||||
}
|
|
|
@ -18,8 +18,6 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository
|
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
|
@ -17,10 +17,9 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
||||||
|
import dev.usbharu.hideout.core.domain.model.media.FileType
|
||||||
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Media.mimeType
|
import dev.usbharu.hideout.core.domain.model.media.MimeType
|
||||||
import dev.usbharu.hideout.core.service.media.FileType
|
|
||||||
import dev.usbharu.hideout.core.service.media.MimeType
|
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.meta.Jwt
|
|
||||||
import dev.usbharu.hideout.core.domain.model.meta.MetaRepository
|
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
|
@ -1,232 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.Emoji
|
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
|
|
||||||
import dev.usbharu.hideout.core.domain.model.reaction.Reaction
|
|
||||||
import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository
|
|
||||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
|
||||||
import org.jetbrains.exposed.sql.*
|
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
|
||||||
import org.slf4j.Logger
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Repository
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
class ReactionRepositoryImpl(
|
|
||||||
private val idGenerateService: IdGenerateService
|
|
||||||
) : ReactionRepository, AbstractRepository() {
|
|
||||||
override val logger: Logger
|
|
||||||
get() = Companion.logger
|
|
||||||
|
|
||||||
override suspend fun generateId(): Long = idGenerateService.generateId()
|
|
||||||
|
|
||||||
override suspend fun save(reaction: Reaction): Reaction = query {
|
|
||||||
if (Reactions.selectAll().where { Reactions.id eq reaction.id }.forUpdate().empty()) {
|
|
||||||
Reactions.insert {
|
|
||||||
it[id] = reaction.id
|
|
||||||
if (reaction.emoji is CustomEmoji) {
|
|
||||||
it[customEmojiId] = reaction.emoji.id
|
|
||||||
it[unicodeEmoji] = null
|
|
||||||
} else {
|
|
||||||
it[customEmojiId] = null
|
|
||||||
it[unicodeEmoji] = reaction.emoji.name
|
|
||||||
}
|
|
||||||
it[postId] = reaction.postId
|
|
||||||
it[actorId] = reaction.actorId
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Reactions.update({ Reactions.id eq reaction.id }) {
|
|
||||||
if (reaction.emoji is CustomEmoji) {
|
|
||||||
it[customEmojiId] = reaction.emoji.id
|
|
||||||
it[unicodeEmoji] = null
|
|
||||||
} else {
|
|
||||||
it[customEmojiId] = null
|
|
||||||
it[unicodeEmoji] = reaction.emoji.name
|
|
||||||
}
|
|
||||||
it[postId] = reaction.postId
|
|
||||||
it[actorId] = reaction.actorId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@query reaction
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun delete(reaction: Reaction): Reaction = query {
|
|
||||||
if (reaction.emoji is CustomEmoji) {
|
|
||||||
Reactions.deleteWhere {
|
|
||||||
id.eq(reaction.id).and(postId.eq(reaction.postId)).and(actorId.eq(reaction.actorId))
|
|
||||||
.and(customEmojiId.eq(reaction.emoji.id))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Reactions.deleteWhere {
|
|
||||||
id.eq(reaction.id).and(postId.eq(reaction.postId)).and(actorId.eq(reaction.actorId))
|
|
||||||
.and(unicodeEmoji.eq(reaction.emoji.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@query reaction
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun deleteByPostId(postId: Long): Int = query {
|
|
||||||
return@query Reactions.deleteWhere {
|
|
||||||
Reactions.postId eq postId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun deleteByActorId(actorId: Long): Int = query {
|
|
||||||
return@query Reactions.deleteWhere {
|
|
||||||
Reactions.actorId eq actorId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun deleteByPostIdAndActorId(postId: Long, actorId: Long): Unit = query {
|
|
||||||
Reactions.deleteWhere {
|
|
||||||
Reactions.postId eq postId and (Reactions.actorId eq actorId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun deleteByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji): Unit = query {
|
|
||||||
if (emoji is CustomEmoji) {
|
|
||||||
Reactions.deleteWhere {
|
|
||||||
Reactions.postId.eq(postId)
|
|
||||||
.and(Reactions.actorId.eq(actorId))
|
|
||||||
.and(customEmojiId.eq(emoji.id))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Reactions.deleteWhere {
|
|
||||||
Reactions.postId.eq(postId)
|
|
||||||
.and(Reactions.actorId.eq(actorId))
|
|
||||||
.and(unicodeEmoji.eq(emoji.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findById(id: Long): Reaction? = query {
|
|
||||||
return@query Reactions.leftJoin(CustomEmojis).selectAll().where { Reactions.id eq id }.singleOrNull()
|
|
||||||
?.toReaction()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByPostId(postId: Long): List<Reaction> = query {
|
|
||||||
return@query Reactions.leftJoin(CustomEmojis).selectAll().where { Reactions.postId eq postId }
|
|
||||||
.map { it.toReaction() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction? =
|
|
||||||
query {
|
|
||||||
return@query Reactions.leftJoin(CustomEmojis).selectAll().where {
|
|
||||||
Reactions.postId eq postId and (Reactions.actorId eq actorId).and(
|
|
||||||
Reactions.customEmojiId.eq<Long?>(
|
|
||||||
emojiId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}.singleOrNull()?.toReaction()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean =
|
|
||||||
query {
|
|
||||||
return@query Reactions.selectAll().where {
|
|
||||||
Reactions.postId
|
|
||||||
.eq<Long>(postId)
|
|
||||||
.and(Reactions.actorId.eq<Long>(actorId))
|
|
||||||
.and(Reactions.customEmojiId.eq<Long?>(emojiId))
|
|
||||||
}.empty().not()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun existByPostIdAndActorIdAndUnicodeEmoji(
|
|
||||||
postId: Long,
|
|
||||||
actorId: Long,
|
|
||||||
unicodeEmoji: String
|
|
||||||
): Boolean = query {
|
|
||||||
return@query Reactions.selectAll().where {
|
|
||||||
Reactions.postId
|
|
||||||
.eq<Long>(postId)
|
|
||||||
.and(Reactions.actorId.eq<Long>(actorId))
|
|
||||||
.and(Reactions.unicodeEmoji.eq<String?>(unicodeEmoji))
|
|
||||||
}.empty().not()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun existByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji): Boolean = query {
|
|
||||||
val query = Reactions.selectAll().where {
|
|
||||||
Reactions.postId
|
|
||||||
.eq(postId)
|
|
||||||
.and(Reactions.actorId.eq(actorId))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (emoji is UnicodeEmoji) {
|
|
||||||
query.andWhere { Reactions.unicodeEmoji eq emoji.name }
|
|
||||||
} else {
|
|
||||||
emoji as CustomEmoji
|
|
||||||
query.andWhere { Reactions.customEmojiId eq emoji.id }
|
|
||||||
}
|
|
||||||
|
|
||||||
return@query query.empty().not()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun existByPostIdAndActor(postId: Long, actorId: Long): Boolean = query {
|
|
||||||
Reactions.selectAll().where { Reactions.postId.eq(postId).and(Reactions.actorId.eq(actorId)) }.empty().not()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List<Reaction> = query {
|
|
||||||
return@query Reactions.leftJoin(CustomEmojis)
|
|
||||||
.selectAll().where { Reactions.postId eq postId and (Reactions.actorId eq actorId) }
|
|
||||||
.map { it.toReaction() }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(ReactionRepositoryImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ResultRow.toReaction(): Reaction {
|
|
||||||
val emoji = if (this[Reactions.customEmojiId] != null) {
|
|
||||||
CustomEmoji(
|
|
||||||
id = this[Reactions.customEmojiId]!!,
|
|
||||||
name = this[CustomEmojis.name],
|
|
||||||
domain = this[CustomEmojis.domain],
|
|
||||||
instanceId = this[CustomEmojis.instanceId],
|
|
||||||
url = this[CustomEmojis.url],
|
|
||||||
category = this[CustomEmojis.category],
|
|
||||||
createdAt = this[CustomEmojis.createdAt]
|
|
||||||
)
|
|
||||||
} else if (this[Reactions.unicodeEmoji] != null) {
|
|
||||||
UnicodeEmoji(this[Reactions.unicodeEmoji]!!)
|
|
||||||
} else {
|
|
||||||
throw IllegalStateException("customEmojiId and unicodeEmoji is null.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return Reaction(
|
|
||||||
this[Reactions.id].value,
|
|
||||||
emoji,
|
|
||||||
this[Reactions.postId],
|
|
||||||
this[Reactions.actorId]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
object Reactions : LongIdTable("reactions") {
|
|
||||||
val customEmojiId = long("custom_emoji_id").references(CustomEmojis.id).nullable()
|
|
||||||
val unicodeEmoji = varchar("unicode_emoji", 255).nullable()
|
|
||||||
val postId: Column<Long> =
|
|
||||||
long("post_id").references(Posts.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
|
|
||||||
val actorId: Column<Long> =
|
|
||||||
long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
|
|
||||||
|
|
||||||
init {
|
|
||||||
uniqueIndex(customEmojiId, postId, actorId)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.infrastructure.mongorepository
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.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>
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.infrastructure.mongorepository
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.resource.ResourceAccessException
|
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
|
||||||
import org.springframework.dao.DataAccessException
|
|
||||||
import org.springframework.dao.DuplicateKeyException
|
|
||||||
import org.springframework.stereotype.Repository
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
@ConditionalOnProperty("hideout.use-mongodb", havingValue = "true", matchIfMissing = false)
|
|
||||||
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> {
|
|
||||||
try {
|
|
||||||
return mongoTimelineRepository.saveAll(timelines)
|
|
||||||
} catch (e: DuplicateKeyException) {
|
|
||||||
throw DuplicateException("Timeline duplicate.", e)
|
|
||||||
} catch (e: DataAccessException) {
|
|
||||||
throw ResourceAccessException(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature
|
package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException
|
import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException
|
||||||
import dev.usbharu.hideout.util.RsaUtil
|
import dev.usbharu.hideout.util.RsaUtil
|
||||||
import dev.usbharu.httpsignature.common.HttpMethod
|
import dev.usbharu.httpsignature.common.HttpMethod
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
|
package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
|
|
@ -19,7 +19,7 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
|
package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||||
import dev.usbharu.hideout.application.external.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
|
@ -40,13 +40,7 @@ class AuthController(
|
||||||
|
|
||||||
@PostMapping("/auth/sign_up")
|
@PostMapping("/auth/sign_up")
|
||||||
suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm): String {
|
suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm): String {
|
||||||
val registerAccount = authApiService.registerAccount(
|
|
||||||
RegisterAccountDto(
|
|
||||||
signUpForm.username,
|
|
||||||
signUpForm.password,
|
|
||||||
signUpForm.recaptchaResponse
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return "redirect:" + registerAccount.url
|
return "redirect:" + registerAccount.url
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.query.model
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterType
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.FilterKeywords
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Filters
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toFilter
|
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toFilterKeyword
|
|
||||||
import org.jetbrains.exposed.sql.Query
|
|
||||||
import org.jetbrains.exposed.sql.and
|
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
|
||||||
import org.springframework.stereotype.Repository
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
class ExposedFilterQueryService : FilterQueryService {
|
|
||||||
override suspend fun findByUserIdAndType(userId: Long, types: List<FilterType>): List<FilterQueryModel> {
|
|
||||||
return Filters
|
|
||||||
.rightJoin(FilterKeywords)
|
|
||||||
.selectAll()
|
|
||||||
.where { Filters.userId eq userId }
|
|
||||||
.toFilterQueryModel()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByUserId(userId: Long): List<FilterQueryModel> {
|
|
||||||
return Filters
|
|
||||||
.rightJoin(FilterKeywords)
|
|
||||||
.selectAll()
|
|
||||||
.where { Filters.userId eq userId }
|
|
||||||
.toFilterQueryModel()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByUserIdAndId(userId: Long, id: Long): FilterQueryModel? {
|
|
||||||
return Filters
|
|
||||||
.leftJoin(FilterKeywords)
|
|
||||||
.selectAll()
|
|
||||||
.where { Filters.userId eq userId and (Filters.id eq id) }
|
|
||||||
.toFilterQueryModel()
|
|
||||||
.firstOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByUserIdAndKeywordId(userId: Long, keywordId: Long): FilterQueryModel? {
|
|
||||||
return Filters
|
|
||||||
.leftJoin(FilterKeywords)
|
|
||||||
.selectAll()
|
|
||||||
.where { Filters.userId eq userId and (FilterKeywords.id eq keywordId) }
|
|
||||||
.toFilterQueryModel()
|
|
||||||
.firstOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Query.toFilterQueryModel(): List<FilterQueryModel> {
|
|
||||||
return this
|
|
||||||
.groupBy { it[Filters.id] }
|
|
||||||
.map { it.value }
|
|
||||||
.map {
|
|
||||||
FilterQueryModel.of(
|
|
||||||
it.first().toFilter(),
|
|
||||||
it.map { resultRow -> resultRow.toFilterKeyword() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.query.model
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.Filter
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterAction
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterType
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword
|
|
||||||
|
|
||||||
data class FilterQueryModel(
|
|
||||||
val id: Long,
|
|
||||||
val userId: Long,
|
|
||||||
val name: String,
|
|
||||||
val context: List<FilterType>,
|
|
||||||
val filterAction: FilterAction,
|
|
||||||
val keywords: List<FilterKeyword>
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
@Suppress("FunctionMinLength")
|
|
||||||
fun of(filter: Filter, keywords: List<FilterKeyword>): FilterQueryModel = FilterQueryModel(
|
|
||||||
id = filter.id,
|
|
||||||
userId = filter.userId,
|
|
||||||
name = filter.name,
|
|
||||||
context = filter.context,
|
|
||||||
filterAction = filter.filterAction,
|
|
||||||
keywords = keywords
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.query.model
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterType
|
|
||||||
|
|
||||||
interface FilterQueryService {
|
|
||||||
suspend fun findByUserIdAndType(userId: Long, types: List<FilterType>): List<FilterQueryModel>
|
|
||||||
suspend fun findByUserId(userId: Long): List<FilterQueryModel>
|
|
||||||
suspend fun findByUserIdAndId(userId: Long, id: Long): FilterQueryModel?
|
|
||||||
suspend fun findByUserIdAndKeywordId(userId: Long, keywordId: Long): FilterQueryModel?
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.filter
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterMode
|
|
||||||
|
|
||||||
data class FilterKeyword(
|
|
||||||
val keyword: String,
|
|
||||||
val mode: FilterMode
|
|
||||||
)
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.filter
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.query.model.FilterQueryModel
|
|
||||||
|
|
||||||
data class FilterResult(
|
|
||||||
val filter: FilterQueryModel,
|
|
||||||
val keyword: String,
|
|
||||||
)
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.filter
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterAction
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterType
|
|
||||||
import dev.usbharu.hideout.core.query.model.FilterQueryModel
|
|
||||||
|
|
||||||
interface MuteService {
|
|
||||||
suspend fun createFilter(
|
|
||||||
title: String,
|
|
||||||
context: List<FilterType>,
|
|
||||||
action: FilterAction,
|
|
||||||
keywords: List<FilterKeyword>,
|
|
||||||
loginUser: Long
|
|
||||||
): FilterQueryModel
|
|
||||||
|
|
||||||
suspend fun getFilters(userId: Long, types: List<FilterType> = emptyList()): List<FilterQueryModel>
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.filter
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.Filter
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterAction
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterRepository
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterType
|
|
||||||
import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeywordRepository
|
|
||||||
import dev.usbharu.hideout.core.query.model.FilterQueryModel
|
|
||||||
import dev.usbharu.hideout.core.query.model.FilterQueryService
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class MuteServiceImpl(
|
|
||||||
private val filterRepository: FilterRepository,
|
|
||||||
private val filterKeywordRepository: FilterKeywordRepository,
|
|
||||||
private val filterQueryService: FilterQueryService
|
|
||||||
) : MuteService {
|
|
||||||
override suspend fun createFilter(
|
|
||||||
title: String,
|
|
||||||
context: List<FilterType>,
|
|
||||||
action: FilterAction,
|
|
||||||
keywords: List<FilterKeyword>,
|
|
||||||
loginUser: Long
|
|
||||||
): FilterQueryModel {
|
|
||||||
val filter = Filter(
|
|
||||||
filterRepository.generateId(),
|
|
||||||
loginUser,
|
|
||||||
title,
|
|
||||||
context,
|
|
||||||
action
|
|
||||||
)
|
|
||||||
|
|
||||||
val filterKeywordList = keywords.map {
|
|
||||||
dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword(
|
|
||||||
filterKeywordRepository.generateId(),
|
|
||||||
filter.id,
|
|
||||||
it.keyword,
|
|
||||||
it.mode
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val savedFilter = filterRepository.save(filter)
|
|
||||||
|
|
||||||
filterKeywordRepository.saveAll(filterKeywordList)
|
|
||||||
return FilterQueryModel.of(savedFilter, filterKeywordList)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getFilters(userId: Long, types: List<FilterType>): List<FilterQueryModel> =
|
|
||||||
filterQueryService.findByUserIdAndType(userId, types)
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.instance
|
|
||||||
|
|
||||||
data class InstanceCreateDto(
|
|
||||||
val name: String?,
|
|
||||||
val description: String?,
|
|
||||||
val url: String,
|
|
||||||
val iconUrl: String,
|
|
||||||
val sharedInbox: String?,
|
|
||||||
val software: String?,
|
|
||||||
val version: String?,
|
|
||||||
)
|
|
|
@ -1,124 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.instance
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.Instance
|
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
|
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo
|
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo2_0
|
|
||||||
import dev.usbharu.hideout.core.service.resource.ResourceResolveService
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.net.URL
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
interface InstanceService {
|
|
||||||
suspend fun fetchInstance(url: String, sharedInbox: String? = null): Instance
|
|
||||||
suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance
|
|
||||||
}
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class InstanceServiceImpl(
|
|
||||||
private val instanceRepository: InstanceRepository,
|
|
||||||
private val resourceResolveService: ResourceResolveService,
|
|
||||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper,
|
|
||||||
) : InstanceService {
|
|
||||||
override suspend fun fetchInstance(url: String, sharedInbox: String?): Instance {
|
|
||||||
val u = URL(url)
|
|
||||||
val resolveInstanceUrl = u.protocol + "://" + u.host
|
|
||||||
|
|
||||||
val instance = instanceRepository.findByUrl(resolveInstanceUrl)
|
|
||||||
|
|
||||||
if (instance != null) {
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl)
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
try {
|
|
||||||
val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText()
|
|
||||||
val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java)
|
|
||||||
val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href }
|
|
||||||
|
|
||||||
for ((key, value) in nodeinfoPathMap) {
|
|
||||||
when (key) {
|
|
||||||
"http://nodeinfo.diaspora.software/ns/schema/2.0",
|
|
||||||
"http://nodeinfo.diaspora.software/ns/schema/2.1",
|
|
||||||
-> {
|
|
||||||
val nodeinfo20 = objectMapper.readValue(
|
|
||||||
resourceResolveService.resolve(value!!).bodyAsText(),
|
|
||||||
Nodeinfo2_0::class.java
|
|
||||||
)
|
|
||||||
|
|
||||||
val instanceCreateDto = InstanceCreateDto(
|
|
||||||
name = nodeinfo20.metadata?.nodeName,
|
|
||||||
description = nodeinfo20.metadata?.nodeDescription,
|
|
||||||
url = resolveInstanceUrl,
|
|
||||||
iconUrl = "$resolveInstanceUrl/favicon.ico",
|
|
||||||
sharedInbox = sharedInbox,
|
|
||||||
software = nodeinfo20.software?.name,
|
|
||||||
version = nodeinfo20.software?.version
|
|
||||||
)
|
|
||||||
return createNewInstance(instanceCreateDto)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
throw IllegalStateException("Unknown nodeinfo versions: $key url: $value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.warn("FAILED Fetch Instance", e)
|
|
||||||
}
|
|
||||||
return createNewInstance(
|
|
||||||
InstanceCreateDto(
|
|
||||||
name = null,
|
|
||||||
description = null,
|
|
||||||
url = resolveInstanceUrl,
|
|
||||||
iconUrl = "$resolveInstanceUrl/favicon.ico",
|
|
||||||
sharedInbox = null,
|
|
||||||
software = null,
|
|
||||||
version = null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance {
|
|
||||||
val instance = Instance(
|
|
||||||
id = instanceRepository.generateId(),
|
|
||||||
name = instanceCreateDto.name ?: instanceCreateDto.url,
|
|
||||||
description = instanceCreateDto.description.orEmpty(),
|
|
||||||
url = instanceCreateDto.url,
|
|
||||||
iconUrl = instanceCreateDto.iconUrl,
|
|
||||||
sharedInbox = instanceCreateDto.sharedInbox,
|
|
||||||
software = instanceCreateDto.software ?: "unknown",
|
|
||||||
version = instanceCreateDto.version ?: "unknown",
|
|
||||||
isBlocked = false,
|
|
||||||
isMuted = false,
|
|
||||||
moderationNote = "",
|
|
||||||
createdAt = Instant.now()
|
|
||||||
)
|
|
||||||
instanceRepository.save(instance)
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(InstanceServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import org.apache.tika.Tika
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
@Component
|
|
||||||
class ApatcheTikaFileTypeDeterminationService : FileTypeDeterminationService {
|
|
||||||
override fun fileType(
|
|
||||||
byteArray: ByteArray,
|
|
||||||
filename: String,
|
|
||||||
contentType: String?
|
|
||||||
): MimeType {
|
|
||||||
logger.info("START Detect file type name: {}", filename)
|
|
||||||
|
|
||||||
val tika = Tika()
|
|
||||||
|
|
||||||
val detect = try {
|
|
||||||
tika.detect(byteArray, filename)
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
logger.warn("FAILED Detect file type", e)
|
|
||||||
"application/octet-stream"
|
|
||||||
}
|
|
||||||
|
|
||||||
val type = detect.substringBefore("/")
|
|
||||||
val fileType = when (type) {
|
|
||||||
"image" -> {
|
|
||||||
FileType.Image
|
|
||||||
}
|
|
||||||
|
|
||||||
"video" -> {
|
|
||||||
FileType.Video
|
|
||||||
}
|
|
||||||
|
|
||||||
"audio" -> {
|
|
||||||
FileType.Audio
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
FileType.Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val mimeType = MimeType(type, detect.substringAfter("/"), fileType)
|
|
||||||
|
|
||||||
logger.info("SUCCESS Detect file type name: {},MimeType: {}", filename, mimeType)
|
|
||||||
return mimeType
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun fileType(path: Path, filename: String): MimeType {
|
|
||||||
logger.info("START Detect file type name: {}", filename)
|
|
||||||
|
|
||||||
val tika = Tika()
|
|
||||||
|
|
||||||
val detect = try {
|
|
||||||
tika.detect(path)
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
logger.warn("FAILED Detect file type", e)
|
|
||||||
"application/octet-stream"
|
|
||||||
}
|
|
||||||
|
|
||||||
val type = detect.substringBefore("/")
|
|
||||||
val fileType = when (type) {
|
|
||||||
"image" -> {
|
|
||||||
FileType.Image
|
|
||||||
}
|
|
||||||
|
|
||||||
"video" -> {
|
|
||||||
FileType.Video
|
|
||||||
}
|
|
||||||
|
|
||||||
"audio" -> {
|
|
||||||
FileType.Audio
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
FileType.Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val mimeType = MimeType(type, detect.substringAfter("/"), fileType)
|
|
||||||
|
|
||||||
logger.info("SUCCESS Detect file type name: {},MimeType: {}", filename, mimeType)
|
|
||||||
return mimeType
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(ApatcheTikaFileTypeDeterminationService::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
interface FileTypeDeterminationService {
|
|
||||||
fun fileType(byteArray: ByteArray, filename: String, contentType: String?): MimeType
|
|
||||||
fun fileType(path: Path, filename: String): MimeType
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
|
||||||
import dev.usbharu.hideout.application.config.LocalStorageConfig
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.StandardOpenOption
|
|
||||||
import kotlin.io.path.copyTo
|
|
||||||
import kotlin.io.path.createDirectories
|
|
||||||
import kotlin.io.path.deleteIfExists
|
|
||||||
import kotlin.io.path.outputStream
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ローカルファイルシステムにメディアを保存します
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
* ApplicationConfigとLocalStorageConfigをもとに作成
|
|
||||||
*
|
|
||||||
* @param applicationConfig ApplicationConfig
|
|
||||||
* @param localStorageConfig LocalStorageConfig
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true)
|
|
||||||
class LocalFileSystemMediaDataStore(
|
|
||||||
applicationConfig: ApplicationConfig,
|
|
||||||
localStorageConfig: LocalStorageConfig
|
|
||||||
) : MediaDataStore {
|
|
||||||
|
|
||||||
private val savePath: Path = Path.of(localStorageConfig.path).toAbsolutePath()
|
|
||||||
|
|
||||||
private val publicUrl = localStorageConfig.publicUrl ?: "${applicationConfig.url}/files/"
|
|
||||||
|
|
||||||
init {
|
|
||||||
savePath.createDirectories()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("NestedBlockDepth")
|
|
||||||
override suspend fun save(dataMediaSave: MediaSave): SavedMedia {
|
|
||||||
val fileSavePath = buildSavePath(savePath, dataMediaSave.name)
|
|
||||||
val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataMediaSave.name)
|
|
||||||
|
|
||||||
dataMediaSave.thumbnailInputStream?.inputStream()?.use {
|
|
||||||
it.buffered().use { bufferedInputStream ->
|
|
||||||
thumbnailSavePath.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)
|
|
||||||
.use { outputStream ->
|
|
||||||
outputStream.buffered().use {
|
|
||||||
bufferedInputStream.transferTo(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dataMediaSave.fileInputStream.inputStream().use {
|
|
||||||
it.buffered().use { bufferedInputStream ->
|
|
||||||
fileSavePath.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)
|
|
||||||
.use { outputStream -> outputStream.buffered().use { bufferedInputStream.transferTo(it) } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SuccessSavedMedia(
|
|
||||||
dataMediaSave.name,
|
|
||||||
publicUrl + dataMediaSave.name,
|
|
||||||
publicUrl + "thumbnail-" + dataMediaSave.name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia {
|
|
||||||
logger.info("START Media upload. {}", dataSaveRequest.name)
|
|
||||||
val fileSavePath = buildSavePath(savePath, dataSaveRequest.name)
|
|
||||||
val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataSaveRequest.name)
|
|
||||||
|
|
||||||
val fileSavePathString = fileSavePath.toAbsolutePath().toString()
|
|
||||||
logger.info("MEDIA save. path: {}", fileSavePathString)
|
|
||||||
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
try {
|
|
||||||
dataSaveRequest.filePath.copyTo(fileSavePath)
|
|
||||||
dataSaveRequest.thumbnailPath?.copyTo(thumbnailSavePath)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.warn("FAILED to Save the media.", e)
|
|
||||||
return FaildSavedMedia("FAILED to Save the media.", "Failed copy to path: $fileSavePathString", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("SUCCESS Media upload. {}", dataSaveRequest.name)
|
|
||||||
return SuccessSavedMedia(
|
|
||||||
dataSaveRequest.name,
|
|
||||||
publicUrl + dataSaveRequest.name,
|
|
||||||
publicUrl + "thumbnail-" + dataSaveRequest.name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* メディアを削除します。サムネイルも削除されます。
|
|
||||||
*
|
|
||||||
* @param id 削除するメディアのid [SuccessSavedMedia.name]を指定します。
|
|
||||||
*/
|
|
||||||
override suspend fun delete(id: String) {
|
|
||||||
logger.info("START Media delete. id: {}", id)
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
try {
|
|
||||||
buildSavePath(savePath, id).deleteIfExists()
|
|
||||||
buildSavePath(savePath, "thumbnail-$id").deleteIfExists()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.warn("FAILED Media delete. id: {}", id, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildSavePath(savePath: Path, name: String): Path = savePath.resolve(name)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(LocalFileSystemMediaDataStore::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
|
|
||||||
interface MediaBlurhashService {
|
|
||||||
fun generateBlurhash(bufferedImage: BufferedImage): String
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import io.trbl.blurhash.BlurHash
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class MediaBlurhashServiceImpl : MediaBlurhashService {
|
|
||||||
override fun generateBlurhash(bufferedImage: BufferedImage): String = BlurHash.encode(bufferedImage)
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
/**
|
|
||||||
* メディアを保存するインタフェース
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
interface MediaDataStore {
|
|
||||||
/**
|
|
||||||
* InputStreamを使用してメディアを保存します
|
|
||||||
*
|
|
||||||
* @param dataMediaSave FileとThumbnailのinputStream
|
|
||||||
* @return 保存されたメディア
|
|
||||||
*/
|
|
||||||
suspend fun save(dataMediaSave: MediaSave): SavedMedia
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一時ファイルのパスを使用してメディアを保存します
|
|
||||||
*
|
|
||||||
* @param dataSaveRequest FileとThumbnailのパス
|
|
||||||
* @return 保存されたメディア
|
|
||||||
*/
|
|
||||||
suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia
|
|
||||||
|
|
||||||
/**
|
|
||||||
* メディアを削除します
|
|
||||||
* 実装はサムネイル、メタデータなども削除するべきです。
|
|
||||||
*
|
|
||||||
* @param id 削除するメディアのid 通常は[SuccessSavedMedia.name]を指定します。
|
|
||||||
*/
|
|
||||||
suspend fun delete(id: String)
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
interface MediaFileRenameService {
|
|
||||||
/**
|
|
||||||
* メディアをリネームします
|
|
||||||
*
|
|
||||||
* @param uploadName アップロードされた時点でのファイル名
|
|
||||||
* @param uploadMimeType アップロードされた時点でのMimeType
|
|
||||||
* @param processedName 処理後のファイル名
|
|
||||||
* @param processedMimeType 処理後のMimeType
|
|
||||||
* @return リネーム後のファイル名
|
|
||||||
*/
|
|
||||||
fun rename(uploadName: String, uploadMimeType: MimeType, processedName: String, processedMimeType: MimeType): String
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
data class MediaSave(
|
|
||||||
val name: String,
|
|
||||||
val prefix: String,
|
|
||||||
val fileInputStream: ByteArray,
|
|
||||||
val thumbnailInputStream: ByteArray?
|
|
||||||
) {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as MediaSave
|
|
||||||
|
|
||||||
if (name != other.name) return false
|
|
||||||
if (prefix != other.prefix) return false
|
|
||||||
if (!fileInputStream.contentEquals(other.fileInputStream)) return false
|
|
||||||
if (thumbnailInputStream != null) {
|
|
||||||
if (other.thumbnailInputStream == null) return false
|
|
||||||
if (!thumbnailInputStream.contentEquals(other.thumbnailInputStream)) return false
|
|
||||||
} else if (other.thumbnailInputStream != null) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = name.hashCode()
|
|
||||||
result = 31 * result + prefix.hashCode()
|
|
||||||
result = 31 * result + fileInputStream.contentHashCode()
|
|
||||||
result = 31 * result + (thumbnailInputStream?.contentHashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
data class MediaSaveRequest(
|
|
||||||
val name: String,
|
|
||||||
val preffix: String,
|
|
||||||
val filePath: Path,
|
|
||||||
val thumbnailPath: Path?
|
|
||||||
)
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.media.Media
|
|
||||||
import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest
|
|
||||||
|
|
||||||
interface MediaService {
|
|
||||||
suspend fun uploadLocalMedia(mediaRequest: MediaRequest): Media
|
|
||||||
suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media
|
|
||||||
}
|
|
|
@ -1,218 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.media.MediaSaveException
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.media.UnsupportedMediaException
|
|
||||||
import dev.usbharu.hideout.core.domain.model.media.Media
|
|
||||||
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
|
||||||
import dev.usbharu.hideout.core.service.media.converter.MediaProcessService
|
|
||||||
import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest
|
|
||||||
import dev.usbharu.hideout.util.withDelete
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.nio.file.Files
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
class MediaServiceImpl(
|
|
||||||
private val mediaDataStore: MediaDataStore,
|
|
||||||
private val fileTypeDeterminationService: FileTypeDeterminationService,
|
|
||||||
private val mediaBlurhashService: MediaBlurhashService,
|
|
||||||
private val mediaRepository: MediaRepository,
|
|
||||||
private val mediaProcessServices: List<MediaProcessService>,
|
|
||||||
private val remoteMediaDownloadService: RemoteMediaDownloadService,
|
|
||||||
private val renameService: MediaFileRenameService
|
|
||||||
) : MediaService {
|
|
||||||
@Suppress("LongMethod", "NestedBlockDepth")
|
|
||||||
override suspend fun uploadLocalMedia(mediaRequest: MediaRequest): EntityMedia {
|
|
||||||
val fileName = mediaRequest.file.name
|
|
||||||
logger.info(
|
|
||||||
"Media upload. filename:$fileName " +
|
|
||||||
"contentType:${mediaRequest.file.contentType}"
|
|
||||||
)
|
|
||||||
|
|
||||||
val tempFile = Files.createTempFile("hideout-tmp-file", ".tmp")
|
|
||||||
|
|
||||||
tempFile.withDelete().use {
|
|
||||||
Files.newOutputStream(tempFile).use { outputStream ->
|
|
||||||
mediaRequest.file.inputStream.use {
|
|
||||||
it.transferTo(outputStream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val mimeType = fileTypeDeterminationService.fileType(tempFile, fileName)
|
|
||||||
|
|
||||||
val process = findMediaProcessor(mimeType).process(
|
|
||||||
mimeType,
|
|
||||||
fileName,
|
|
||||||
tempFile,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
|
|
||||||
val dataMediaSave = MediaSaveRequest(
|
|
||||||
renameService.rename(
|
|
||||||
mediaRequest.file.name,
|
|
||||||
mimeType,
|
|
||||||
process.filePath.fileName.toString(),
|
|
||||||
process.fileMimeType
|
|
||||||
),
|
|
||||||
"",
|
|
||||||
process.filePath,
|
|
||||||
process.thumbnailPath
|
|
||||||
)
|
|
||||||
dataMediaSave.filePath.withDelete().use {
|
|
||||||
dataMediaSave.thumbnailPath.withDelete().use {
|
|
||||||
val save = try {
|
|
||||||
mediaDataStore.save(dataMediaSave)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.warn("Failed to save the media", e)
|
|
||||||
throw MediaSaveException("Failed to save the media.", e)
|
|
||||||
}
|
|
||||||
if (save.success.not()) {
|
|
||||||
save as FaildSavedMedia
|
|
||||||
logger.warn("Failed to save the media. reason: ${save.reason}")
|
|
||||||
logger.warn(save.description, save.trace)
|
|
||||||
throw MediaSaveException("Failed to save the media.")
|
|
||||||
}
|
|
||||||
save as SuccessSavedMedia
|
|
||||||
val blurHash = generateBlurhash(process)
|
|
||||||
return mediaRepository.save(
|
|
||||||
EntityMedia(
|
|
||||||
id = mediaRepository.generateId(),
|
|
||||||
name = fileName,
|
|
||||||
url = save.url,
|
|
||||||
remoteUrl = null,
|
|
||||||
thumbnailUrl = save.thumbnailUrl,
|
|
||||||
type = process.fileMimeType.fileType,
|
|
||||||
mimeType = process.fileMimeType,
|
|
||||||
blurHash = blurHash,
|
|
||||||
description = mediaRequest.description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 仮の処理として保存したように動かす
|
|
||||||
@Suppress("LongMethod", "NestedBlockDepth")
|
|
||||||
override suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media {
|
|
||||||
logger.info("MEDIA Remote media. filename:${remoteMedia.name} url:${remoteMedia.url}")
|
|
||||||
|
|
||||||
val findByRemoteUrl = mediaRepository.findByRemoteUrl(remoteMedia.url)
|
|
||||||
if (findByRemoteUrl != null) {
|
|
||||||
logger.warn("DUPLICATED Remote media is duplicated. url: {}", remoteMedia.url)
|
|
||||||
return findByRemoteUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteMediaDownloadService.download(remoteMedia.url).withDelete().use {
|
|
||||||
val mimeType = fileTypeDeterminationService.fileType(it.path, remoteMedia.name)
|
|
||||||
|
|
||||||
val process = findMediaProcessor(mimeType).process(mimeType, remoteMedia.name, it.path, null)
|
|
||||||
|
|
||||||
val mediaSaveRequest = MediaSaveRequest(
|
|
||||||
renameService.rename(
|
|
||||||
remoteMedia.name,
|
|
||||||
mimeType,
|
|
||||||
process.filePath.fileName.toString(),
|
|
||||||
process.fileMimeType
|
|
||||||
),
|
|
||||||
"",
|
|
||||||
process.filePath,
|
|
||||||
process.thumbnailPath
|
|
||||||
)
|
|
||||||
|
|
||||||
mediaSaveRequest.filePath.withDelete().use {
|
|
||||||
mediaSaveRequest.filePath.withDelete().use {
|
|
||||||
val save = try {
|
|
||||||
mediaDataStore.save(mediaSaveRequest)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.warn("Failed to save the media", e)
|
|
||||||
throw MediaSaveException("Failed to save the media.", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (save is FaildSavedMedia) {
|
|
||||||
logger.warn("Failed to save the media. reason: ${save.reason}")
|
|
||||||
logger.warn(save.description, save.trace)
|
|
||||||
throw MediaSaveException("Failed to save the media.")
|
|
||||||
}
|
|
||||||
save as SuccessSavedMedia
|
|
||||||
val blurhash = generateBlurhash(process)
|
|
||||||
return mediaRepository.save(
|
|
||||||
EntityMedia(
|
|
||||||
id = mediaRepository.generateId(),
|
|
||||||
name = remoteMedia.name,
|
|
||||||
url = save.url,
|
|
||||||
remoteUrl = remoteMedia.url,
|
|
||||||
thumbnailUrl = save.thumbnailUrl,
|
|
||||||
type = process.fileMimeType.fileType,
|
|
||||||
mimeType = process.fileMimeType,
|
|
||||||
blurHash = blurhash
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findMediaProcessor(mimeType: MimeType): MediaProcessService {
|
|
||||||
try {
|
|
||||||
return mediaProcessServices.first {
|
|
||||||
try {
|
|
||||||
it.isSupport(mimeType)
|
|
||||||
} catch (_: Exception) {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (_: NoSuchElementException) {
|
|
||||||
throw UnsupportedMediaException("MediaType: $mimeType isn't supported.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateBlurhash(process: ProcessedMediaPath): String {
|
|
||||||
val path = if (process.thumbnailPath != null && process.thumbnailMimeType != null) {
|
|
||||||
process.thumbnailPath
|
|
||||||
} else {
|
|
||||||
process.filePath
|
|
||||||
}
|
|
||||||
val mimeType = if (process.thumbnailPath != null && process.thumbnailMimeType != null) {
|
|
||||||
process.thumbnailMimeType
|
|
||||||
} else {
|
|
||||||
process.fileMimeType
|
|
||||||
}
|
|
||||||
|
|
||||||
val imageReadersByMIMEType = ImageIO.getImageReadersByMIMEType(mimeType.type + "/" + mimeType.subtype)
|
|
||||||
for (imageReader in imageReadersByMIMEType) {
|
|
||||||
try {
|
|
||||||
val bufferedImage = ImageIO.createImageInputStream(path.toFile()).use {
|
|
||||||
imageReader.input = it
|
|
||||||
imageReader.read(0)
|
|
||||||
}
|
|
||||||
return mediaBlurhashService.generateBlurhash(bufferedImage)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.warn("Failed to read thumbnail", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(MediaServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
data class ProcessedFile(
|
|
||||||
val byteArray: ByteArray,
|
|
||||||
val extension: String
|
|
||||||
) {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as ProcessedFile
|
|
||||||
|
|
||||||
if (!byteArray.contentEquals(other.byteArray)) return false
|
|
||||||
if (extension != other.extension) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = byteArray.contentHashCode()
|
|
||||||
result = 31 * result + extension.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
data class ProcessedMedia(
|
|
||||||
val file: ProcessedFile,
|
|
||||||
val thumbnail: ProcessedFile?
|
|
||||||
)
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
data class ProcessedMediaPath(
|
|
||||||
val filePath: Path,
|
|
||||||
val thumbnailPath: Path?,
|
|
||||||
val fileMimeType: MimeType,
|
|
||||||
val thumbnailMimeType: MimeType?
|
|
||||||
)
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
data class RemoteMedia(
|
|
||||||
val name: String,
|
|
||||||
val url: String,
|
|
||||||
val mediaType: String,
|
|
||||||
val description: String? = null
|
|
||||||
)
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
interface RemoteMediaDownloadService {
|
|
||||||
suspend fun download(url: String): Path
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.config.MediaConfig
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.media.RemoteMediaFileSizeException
|
|
||||||
import dev.usbharu.hideout.core.service.resource.KtorResourceResolveService
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.io.path.outputStream
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class RemoteMediaDownloadServiceImpl(
|
|
||||||
private val resourceResolveService: KtorResourceResolveService,
|
|
||||||
private val mediaConfig: MediaConfig
|
|
||||||
) :
|
|
||||||
RemoteMediaDownloadService {
|
|
||||||
override suspend fun download(url: String): Path {
|
|
||||||
logger.info("START Download remote file. url: {}", url)
|
|
||||||
val httpResponse = resourceResolveService.resolve(url).body()
|
|
||||||
val createTempFile = Files.createTempFile("hideout-remote-download", ".tmp")
|
|
||||||
|
|
||||||
logger.debug("Save to {} url: {} ", createTempFile, url)
|
|
||||||
|
|
||||||
httpResponse.use { inputStream ->
|
|
||||||
createTempFile.outputStream().use {
|
|
||||||
inputStream.transferTo(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val contentLength = createTempFile.toFile().length()
|
|
||||||
if (contentLength >= mediaConfig.remoteMediaFileSizeLimit) {
|
|
||||||
throw RemoteMediaFileSizeException(
|
|
||||||
"File size is too large. $contentLength >= ${mediaConfig.remoteMediaFileSizeLimit}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("SUCCESS Download remote file. url: {}", url)
|
|
||||||
return createTempFile
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(RemoteMediaDownloadServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.config.S3StorageConfig
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.awaitAll
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import software.amazon.awssdk.core.sync.RequestBody
|
|
||||||
import software.amazon.awssdk.services.s3.S3Client
|
|
||||||
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest
|
|
||||||
import software.amazon.awssdk.services.s3.model.GetUrlRequest
|
|
||||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@ConditionalOnProperty("hideout.storage.type", havingValue = "s3")
|
|
||||||
class S3MediaDataStore(private val s3Client: S3Client, private val s3StorageConfig: S3StorageConfig) : MediaDataStore {
|
|
||||||
override suspend fun save(dataMediaSave: MediaSave): SavedMedia {
|
|
||||||
val fileUploadRequest = PutObjectRequest.builder()
|
|
||||||
.bucket(s3StorageConfig.bucket)
|
|
||||||
.key(dataMediaSave.name)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val thumbnailKey = "thumbnail-${dataMediaSave.name}"
|
|
||||||
val thumbnailUploadRequest = PutObjectRequest.builder()
|
|
||||||
.bucket(s3StorageConfig.bucket)
|
|
||||||
.key(thumbnailKey)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
awaitAll(
|
|
||||||
async {
|
|
||||||
if (dataMediaSave.thumbnailInputStream != null) {
|
|
||||||
s3Client.putObject(
|
|
||||||
thumbnailUploadRequest,
|
|
||||||
RequestBody.fromBytes(dataMediaSave.thumbnailInputStream)
|
|
||||||
)
|
|
||||||
s3Client.utilities()
|
|
||||||
.getUrl(GetUrlRequest.builder().bucket(s3StorageConfig.bucket).key(thumbnailKey).build())
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async {
|
|
||||||
s3Client.putObject(fileUploadRequest, RequestBody.fromBytes(dataMediaSave.fileInputStream))
|
|
||||||
s3Client.utilities()
|
|
||||||
.getUrl(GetUrlRequest.builder().bucket(s3StorageConfig.bucket).key(dataMediaSave.name).build())
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return SuccessSavedMedia(
|
|
||||||
name = dataMediaSave.name,
|
|
||||||
url = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${dataMediaSave.name}",
|
|
||||||
thumbnailUrl = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$thumbnailKey"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia {
|
|
||||||
logger.info("MEDIA upload. {}", dataSaveRequest.name)
|
|
||||||
|
|
||||||
val fileUploadRequest = PutObjectRequest.builder()
|
|
||||||
.bucket(s3StorageConfig.bucket)
|
|
||||||
.key(dataSaveRequest.name)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, dataSaveRequest.name)
|
|
||||||
|
|
||||||
val thumbnailKey = "thumbnail-${dataSaveRequest.name}"
|
|
||||||
val thumbnailUploadRequest = PutObjectRequest.builder()
|
|
||||||
.bucket(s3StorageConfig.bucket)
|
|
||||||
.key(thumbnailKey)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, thumbnailKey)
|
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
awaitAll(
|
|
||||||
async {
|
|
||||||
if (dataSaveRequest.thumbnailPath != null) {
|
|
||||||
s3Client.putObject(
|
|
||||||
thumbnailUploadRequest,
|
|
||||||
RequestBody.fromFile(dataSaveRequest.thumbnailPath)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async {
|
|
||||||
s3Client.putObject(fileUploadRequest, RequestBody.fromFile(dataSaveRequest.filePath))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val successSavedMedia = SuccessSavedMedia(
|
|
||||||
name = dataSaveRequest.name,
|
|
||||||
url = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${dataSaveRequest.name}",
|
|
||||||
thumbnailUrl = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$thumbnailKey"
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info("SUCCESS Media upload. {}", dataSaveRequest.name)
|
|
||||||
logger.debug(
|
|
||||||
"name: {} url: {} thumbnail url: {}",
|
|
||||||
successSavedMedia.name,
|
|
||||||
successSavedMedia.url,
|
|
||||||
successSavedMedia.thumbnailUrl
|
|
||||||
)
|
|
||||||
|
|
||||||
return successSavedMedia
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun delete(id: String) {
|
|
||||||
val fileDeleteRequest = DeleteObjectRequest.builder().bucket(s3StorageConfig.bucket).key(id).build()
|
|
||||||
val thumbnailDeleteRequest =
|
|
||||||
DeleteObjectRequest.builder().bucket(s3StorageConfig.bucket).key("thumbnail-$id").build()
|
|
||||||
s3Client.deleteObject(fileDeleteRequest)
|
|
||||||
s3Client.deleteObject(thumbnailDeleteRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(S3MediaDataStore::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
sealed class SavedMedia(val success: Boolean) {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as SavedMedia
|
|
||||||
|
|
||||||
return success == other.success
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = success.hashCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
class SuccessSavedMedia(
|
|
||||||
val name: String,
|
|
||||||
val url: String,
|
|
||||||
val thumbnailUrl: String,
|
|
||||||
) :
|
|
||||||
SavedMedia(true) {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as SuccessSavedMedia
|
|
||||||
|
|
||||||
if (name != other.name) return false
|
|
||||||
if (url != other.url) return false
|
|
||||||
if (thumbnailUrl != other.thumbnailUrl) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + name.hashCode()
|
|
||||||
result = 31 * result + url.hashCode()
|
|
||||||
result = 31 * result + thumbnailUrl.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FaildSavedMedia(
|
|
||||||
val reason: String,
|
|
||||||
val description: String,
|
|
||||||
val trace: Throwable? = null
|
|
||||||
) : SavedMedia(false) {
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
if (!super.equals(other)) return false
|
|
||||||
|
|
||||||
other as FaildSavedMedia
|
|
||||||
|
|
||||||
if (reason != other.reason) return false
|
|
||||||
if (description != other.description) return false
|
|
||||||
if (trace != other.trace) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = super.hashCode()
|
|
||||||
result = 31 * result + reason.hashCode()
|
|
||||||
result = 31 * result + description.hashCode()
|
|
||||||
result = 31 * result + (trace?.hashCode() ?: 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
interface ThumbnailGenerateService {
|
|
||||||
fun generate(bufferedImage: InputStream, width: Int, height: Int): ProcessedFile?
|
|
||||||
fun generate(outputStream: ByteArray, width: Int, height: Int): ProcessedFile?
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class ThumbnailGenerateServiceImpl : ThumbnailGenerateService {
|
|
||||||
override fun generate(bufferedImage: InputStream, width: Int, height: Int): ProcessedFile? {
|
|
||||||
val image = ImageIO.read(bufferedImage)
|
|
||||||
return internalGenerate(image)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun generate(outputStream: ByteArray, width: Int, height: Int): ProcessedFile? {
|
|
||||||
val image = ImageIO.read(outputStream.inputStream())
|
|
||||||
return internalGenerate(image)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun internalGenerate(image: BufferedImage): ProcessedFile {
|
|
||||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
|
||||||
ImageIO.write(image, "jpeg", byteArrayOutputStream)
|
|
||||||
return ProcessedFile(byteArrayOutputStream.toByteArray(), "jpg")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Qualifier("uuid")
|
|
||||||
@Service
|
|
||||||
class UUIDMediaFileRenameService : MediaFileRenameService {
|
|
||||||
override fun rename(
|
|
||||||
uploadName: String,
|
|
||||||
uploadMimeType: MimeType,
|
|
||||||
processedName: String,
|
|
||||||
processedMimeType: MimeType
|
|
||||||
): String = "${UUID.randomUUID()}.${uploadMimeType.subtype}.${processedMimeType.subtype}"
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media.converter
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.service.media.FileType
|
|
||||||
import dev.usbharu.hideout.core.service.media.ProcessedFile
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
interface MediaConverter {
|
|
||||||
fun isSupport(fileType: FileType): Boolean
|
|
||||||
fun convert(inputStream: InputStream): ProcessedFile
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media.converter
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.service.media.FileType
|
|
||||||
import dev.usbharu.hideout.core.service.media.ProcessedFile
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
interface MediaConverterRoot {
|
|
||||||
suspend fun convert(
|
|
||||||
fileType: FileType,
|
|
||||||
contentType: String,
|
|
||||||
filename: String,
|
|
||||||
inputStream: InputStream
|
|
||||||
): ProcessedFile
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media.converter
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.service.media.FileType
|
|
||||||
import dev.usbharu.hideout.core.service.media.ProcessedFile
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class MediaConverterRootImpl(private val converters: List<MediaConverter>) : MediaConverterRoot {
|
|
||||||
override suspend fun convert(
|
|
||||||
fileType: FileType,
|
|
||||||
contentType: String,
|
|
||||||
filename: String,
|
|
||||||
inputStream: InputStream
|
|
||||||
): ProcessedFile {
|
|
||||||
val convert = converters.find {
|
|
||||||
it.isSupport(fileType)
|
|
||||||
}?.convert(inputStream)
|
|
||||||
if (convert != null) {
|
|
||||||
return convert
|
|
||||||
}
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
if (filename.contains('.')) {
|
|
||||||
ProcessedFile(inputStream.readAllBytes(), filename.substringAfterLast("."))
|
|
||||||
} else {
|
|
||||||
ProcessedFile(inputStream.readAllBytes(), contentType.substringAfterLast("/"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media.converter
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.service.media.FileType
|
|
||||||
import dev.usbharu.hideout.core.service.media.MimeType
|
|
||||||
import dev.usbharu.hideout.core.service.media.ProcessedMedia
|
|
||||||
import dev.usbharu.hideout.core.service.media.ProcessedMediaPath
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
interface MediaProcessService {
|
|
||||||
fun isSupport(mimeType: MimeType): Boolean
|
|
||||||
|
|
||||||
suspend fun process(
|
|
||||||
fileType: FileType,
|
|
||||||
contentType: String,
|
|
||||||
fileName: String,
|
|
||||||
file: ByteArray,
|
|
||||||
thumbnail: ByteArray?
|
|
||||||
): ProcessedMedia
|
|
||||||
|
|
||||||
suspend fun process(
|
|
||||||
mimeType: MimeType,
|
|
||||||
fileName: String,
|
|
||||||
filePath: Path,
|
|
||||||
thumbnails: Path?
|
|
||||||
): ProcessedMediaPath
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media.converter
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.media.MediaConvertException
|
|
||||||
import dev.usbharu.hideout.core.service.media.*
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
class MediaProcessServiceImpl(
|
|
||||||
private val mediaConverterRoot: MediaConverterRoot,
|
|
||||||
private val thumbnailGenerateService: ThumbnailGenerateService
|
|
||||||
) : MediaProcessService {
|
|
||||||
override fun isSupport(mimeType: MimeType): Boolean = false
|
|
||||||
|
|
||||||
override suspend fun process(
|
|
||||||
fileType: FileType,
|
|
||||||
contentType: String,
|
|
||||||
filename: String,
|
|
||||||
file: ByteArray,
|
|
||||||
thumbnail: ByteArray?
|
|
||||||
): ProcessedMedia {
|
|
||||||
val fileInputStream = try {
|
|
||||||
mediaConverterRoot.convert(fileType, contentType, filename, file.inputStream().buffered())
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.warn("Failed convert media.", e)
|
|
||||||
throw MediaConvertException("Failed convert media.", e)
|
|
||||||
}
|
|
||||||
val thumbnailInputStream = try {
|
|
||||||
thumbnail?.let { mediaConverterRoot.convert(fileType, contentType, filename, it.inputStream().buffered()) }
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.warn("Failed convert thumbnail media.", e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
return ProcessedMedia(
|
|
||||||
fileInputStream,
|
|
||||||
thumbnailGenerateService.generate(
|
|
||||||
thumbnailInputStream?.byteArray ?: file,
|
|
||||||
2048,
|
|
||||||
2048
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun process(
|
|
||||||
mimeType: MimeType,
|
|
||||||
fileName: String,
|
|
||||||
filePath: Path,
|
|
||||||
thumbnails: Path?
|
|
||||||
): ProcessedMediaPath {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(MediaProcessServiceImpl::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media.converter.image
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.exception.media.MediaProcessException
|
|
||||||
import dev.usbharu.hideout.core.service.media.FileType
|
|
||||||
import dev.usbharu.hideout.core.service.media.MimeType
|
|
||||||
import dev.usbharu.hideout.core.service.media.ProcessedMedia
|
|
||||||
import dev.usbharu.hideout.core.service.media.ProcessedMediaPath
|
|
||||||
import dev.usbharu.hideout.core.service.media.converter.MediaProcessService
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.slf4j.MDCContext
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import net.coobird.thumbnailator.Thumbnails
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.awt.Color
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.util.*
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
import kotlin.io.path.inputStream
|
|
||||||
import kotlin.io.path.outputStream
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Qualifier("image")
|
|
||||||
class ImageMediaProcessService(private val imageMediaProcessorConfiguration: ImageMediaProcessorConfiguration?) :
|
|
||||||
MediaProcessService {
|
|
||||||
|
|
||||||
private val convertType = imageMediaProcessorConfiguration?.convert ?: "jpeg"
|
|
||||||
|
|
||||||
private val supportedTypes = imageMediaProcessorConfiguration?.supportedType ?: listOf("webp", "jpeg", "png")
|
|
||||||
|
|
||||||
private val genThumbnail = imageMediaProcessorConfiguration?.thubnail?.generate ?: true
|
|
||||||
|
|
||||||
private val width = imageMediaProcessorConfiguration?.thubnail?.width ?: 1000
|
|
||||||
private val height = imageMediaProcessorConfiguration?.thubnail?.height ?: 1000
|
|
||||||
|
|
||||||
override fun isSupport(mimeType: MimeType): Boolean {
|
|
||||||
if (mimeType.type != "image") {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return supportedTypes.contains(mimeType.subtype)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun process(
|
|
||||||
fileType: FileType,
|
|
||||||
contentType: String,
|
|
||||||
fileName: String,
|
|
||||||
file: ByteArray,
|
|
||||||
thumbnail: ByteArray?
|
|
||||||
): ProcessedMedia {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun process(
|
|
||||||
mimeType: MimeType,
|
|
||||||
fileName: String,
|
|
||||||
filePath: Path,
|
|
||||||
thumbnails: Path?
|
|
||||||
): ProcessedMediaPath = withContext(Dispatchers.IO + MDCContext()) {
|
|
||||||
val read = ImageIO.read(filePath.inputStream())
|
|
||||||
|
|
||||||
val bufferedImage = BufferedImage(read.width, read.height, BufferedImage.TYPE_INT_RGB)
|
|
||||||
|
|
||||||
val graphics = bufferedImage.createGraphics()
|
|
||||||
|
|
||||||
graphics.drawImage(read, 0, 0, Color.BLACK, null)
|
|
||||||
|
|
||||||
val tempFileName = UUID.randomUUID().toString()
|
|
||||||
val tempFile = Files.createTempFile(tempFileName, "tmp")
|
|
||||||
|
|
||||||
val thumbnailPath = if (genThumbnail) {
|
|
||||||
val tempThumbnailFile = Files.createTempFile("thumbnail-$tempFileName", ".tmp")
|
|
||||||
|
|
||||||
tempThumbnailFile.outputStream().use {
|
|
||||||
val write = ImageIO.write(
|
|
||||||
if (thumbnails != null) {
|
|
||||||
Thumbnails.of(thumbnails.toFile())
|
|
||||||
.size(width, height)
|
|
||||||
.imageType(BufferedImage.TYPE_INT_RGB)
|
|
||||||
.asBufferedImage()
|
|
||||||
} else {
|
|
||||||
Thumbnails.of(bufferedImage)
|
|
||||||
.size(width, height)
|
|
||||||
.imageType(BufferedImage.TYPE_INT_RGB)
|
|
||||||
.asBufferedImage()
|
|
||||||
},
|
|
||||||
convertType,
|
|
||||||
it
|
|
||||||
)
|
|
||||||
tempThumbnailFile.takeIf { write }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
tempFile.outputStream().use {
|
|
||||||
if (ImageIO.write(bufferedImage, convertType, it).not()) {
|
|
||||||
logger.warn("Failed to save a temporary file. type: {} ,path: {}", convertType, tempFile)
|
|
||||||
throw MediaProcessException("Failed to save a temporary file.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProcessedMediaPath(
|
|
||||||
tempFile,
|
|
||||||
thumbnailPath,
|
|
||||||
MimeType("image", convertType, FileType.Image),
|
|
||||||
MimeType("image", convertType, FileType.Image).takeIf { genThumbnail }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(ImageMediaProcessService::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media.converter.image
|
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
|
||||||
|
|
||||||
@ConfigurationProperties("hideout.media.image")
|
|
||||||
data class ImageMediaProcessorConfiguration(
|
|
||||||
val convert: String?,
|
|
||||||
val thubnail: ImageMediaProcessorThumbnailConfiguration?,
|
|
||||||
val supportedType: List<String>?,
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
data class ImageMediaProcessorThumbnailConfiguration(
|
|
||||||
val generate: Boolean,
|
|
||||||
val width: Int,
|
|
||||||
val height: Int
|
|
||||||
)
|
|
|
@ -1,143 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 usbharu
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.service.media.converter.movie
|
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.service.media.FileType
|
|
||||||
import dev.usbharu.hideout.core.service.media.MimeType
|
|
||||||
import dev.usbharu.hideout.core.service.media.ProcessedMedia
|
|
||||||
import dev.usbharu.hideout.core.service.media.ProcessedMediaPath
|
|
||||||
import dev.usbharu.hideout.core.service.media.converter.MediaProcessService
|
|
||||||
import org.bytedeco.ffmpeg.global.avcodec
|
|
||||||
import org.bytedeco.javacv.FFmpegFrameFilter
|
|
||||||
import org.bytedeco.javacv.FFmpegFrameGrabber
|
|
||||||
import org.bytedeco.javacv.FFmpegFrameRecorder
|
|
||||||
import org.bytedeco.javacv.Java2DFrameConverter
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Qualifier("video")
|
|
||||||
class MovieMediaProcessService : MediaProcessService {
|
|
||||||
override fun isSupport(mimeType: MimeType): Boolean = mimeType.type == "video"
|
|
||||||
|
|
||||||
override suspend fun process(
|
|
||||||
fileType: FileType,
|
|
||||||
contentType: String,
|
|
||||||
fileName: String,
|
|
||||||
file: ByteArray,
|
|
||||||
thumbnail: ByteArray?
|
|
||||||
): ProcessedMedia {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("LongMethod", "NestedBlockDepth", "CognitiveComplexMethod")
|
|
||||||
override suspend fun process(
|
|
||||||
mimeType: MimeType,
|
|
||||||
fileName: String,
|
|
||||||
filePath: Path,
|
|
||||||
thumbnails: Path?
|
|
||||||
): ProcessedMediaPath {
|
|
||||||
val tempFile = Files.createTempFile("hideout-movie-processor-", ".tmp")
|
|
||||||
val thumbnailFile = Files.createTempFile("hideout-movie-thumbnail-generate-", ".tmp")
|
|
||||||
logger.info("START Convert Movie Media {}", fileName)
|
|
||||||
FFmpegFrameGrabber(filePath.toFile()).use { grabber ->
|
|
||||||
grabber.start()
|
|
||||||
val width = grabber.imageWidth
|
|
||||||
val height = grabber.imageHeight
|
|
||||||
val frameRate = 60.0
|
|
||||||
|
|
||||||
logger.debug("Movie Media Width {}, Height {}", width, height)
|
|
||||||
|
|
||||||
FFmpegFrameFilter(
|
|
||||||
"fps=fps=${frameRate.toInt()}",
|
|
||||||
"anull",
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
grabber.audioChannels
|
|
||||||
).use { filter ->
|
|
||||||
|
|
||||||
filter.sampleFormat = grabber.sampleFormat
|
|
||||||
filter.sampleRate = grabber.sampleRate
|
|
||||||
filter.pixelFormat = grabber.pixelFormat
|
|
||||||
filter.frameRate = grabber.frameRate
|
|
||||||
filter.start()
|
|
||||||
|
|
||||||
val videoBitRate = min(1300000, (width * height * frameRate * 1 * 0.07).toInt())
|
|
||||||
|
|
||||||
logger.debug("Movie Media BitRate {}", videoBitRate)
|
|
||||||
|
|
||||||
FFmpegFrameRecorder(tempFile.toFile(), width, height, grabber.audioChannels).use {
|
|
||||||
it.sampleRate = grabber.sampleRate
|
|
||||||
it.format = "mp4"
|
|
||||||
it.videoCodec = avcodec.AV_CODEC_ID_H264
|
|
||||||
it.audioCodec = avcodec.AV_CODEC_ID_AAC
|
|
||||||
it.audioChannels = grabber.audioChannels
|
|
||||||
it.videoQuality = 1.0
|
|
||||||
it.frameRate = frameRate
|
|
||||||
it.setVideoOption("preset", "ultrafast")
|
|
||||||
it.timestamp = 0
|
|
||||||
it.gopSize = frameRate.toInt()
|
|
||||||
it.videoBitrate = videoBitRate
|
|
||||||
it.start()
|
|
||||||
|
|
||||||
var bufferedImage: BufferedImage? = null
|
|
||||||
|
|
||||||
val frameConverter = Java2DFrameConverter()
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
val grab = grabber.grab() ?: break
|
|
||||||
|
|
||||||
if (bufferedImage == null) {
|
|
||||||
bufferedImage = frameConverter.convert(grab)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (grab.image != null || grab.samples != null) {
|
|
||||||
filter.push(grab)
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
val frame = filter.pull() ?: break
|
|
||||||
it.record(frame)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bufferedImage != null) {
|
|
||||||
ImageIO.write(bufferedImage, "jpeg", thumbnailFile.toFile())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("SUCCESS Convert Movie Media {}", fileName)
|
|
||||||
|
|
||||||
return ProcessedMediaPath(
|
|
||||||
tempFile,
|
|
||||||
thumbnailFile,
|
|
||||||
MimeType("video", "mp4", FileType.Video),
|
|
||||||
MimeType("image", "jpeg", FileType.Image)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = LoggerFactory.getLogger(MovieMediaProcessService::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue