mirror of https://github.com/usbharu/Hideout.git
Merge pull request #168 from usbharu/feature/refactor2
Feature/refactor2
This commit is contained in:
commit
e2f355f4d3
|
@ -3,9 +3,7 @@ build:
|
|||
weights:
|
||||
Indentation: 0
|
||||
MagicNumber: 0
|
||||
InjectDispatcher: 0
|
||||
EnumEntryNameCase: 0
|
||||
ReplaceSafeCallChainWithRun: 0
|
||||
VariableNaming: 0
|
||||
NoNameShadowing: 0
|
||||
|
||||
|
@ -78,7 +76,7 @@ complexity:
|
|||
active: true
|
||||
|
||||
ReplaceSafeCallChainWithRun:
|
||||
active: true
|
||||
active: false
|
||||
|
||||
StringLiteralDuplication:
|
||||
active: false
|
||||
|
@ -172,3 +170,5 @@ potential-bugs:
|
|||
coroutines:
|
||||
RedundantSuspendModifier:
|
||||
active: false
|
||||
InjectDispatcher:
|
||||
active: false
|
||||
|
|
|
@ -6,6 +6,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|||
|
||||
open class Accept : Object {
|
||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
||||
@Suppress("VariableNaming")
|
||||
var `object`: Object? = null
|
||||
|
||||
protected constructor()
|
||||
|
|
|
@ -6,6 +6,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|||
|
||||
open class Create : Object {
|
||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
||||
@Suppress("VariableNaming")
|
||||
var `object`: Object? = null
|
||||
var to: List<String> = emptyList()
|
||||
var cc: List<String> = emptyList()
|
||||
|
|
|
@ -6,6 +6,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
|||
|
||||
open class Delete : Object {
|
||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
||||
@Suppress("VariableNaming")
|
||||
var `object`: Object? = null
|
||||
var published: String? = null
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package dev.usbharu.hideout.activitypub.domain.model
|
|||
import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
||||
|
||||
open class Follow : Object {
|
||||
@Suppress("VariableNaming")
|
||||
var `object`: String? = null
|
||||
|
||||
protected constructor() : super()
|
||||
|
|
|
@ -5,6 +5,7 @@ import dev.usbharu.hideout.activitypub.domain.model.objects.Object
|
|||
import dev.usbharu.hideout.activitypub.domain.model.objects.ObjectDeserializer
|
||||
|
||||
open class Like : Object {
|
||||
@Suppress("VariableNaming")
|
||||
var `object`: String? = null
|
||||
var content: String? = null
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.time.Instant
|
|||
open class Undo : Object {
|
||||
|
||||
@JsonDeserialize(using = ObjectDeserializer::class)
|
||||
@Suppress("VariableNaming")
|
||||
var `object`: Object? = null
|
||||
var published: String? = null
|
||||
|
||||
|
|
|
@ -33,15 +33,8 @@ class ObjectDeserializer : JsonDeserializer<Object>() {
|
|||
}
|
||||
|
||||
return when (activityType) {
|
||||
ExtendedActivityVocabulary.Follow -> {
|
||||
val readValue = p.codec.treeToValue(treeNode, Follow::class.java)
|
||||
readValue
|
||||
}
|
||||
|
||||
ExtendedActivityVocabulary.Note -> {
|
||||
p.codec.treeToValue(treeNode, Note::class.java)
|
||||
}
|
||||
|
||||
ExtendedActivityVocabulary.Follow -> p.codec.treeToValue(treeNode, Follow::class.java)
|
||||
ExtendedActivityVocabulary.Note -> p.codec.treeToValue(treeNode, Note::class.java)
|
||||
ExtendedActivityVocabulary.Object -> p.codec.treeToValue(treeNode, Object::class.java)
|
||||
ExtendedActivityVocabulary.Link -> TODO()
|
||||
ExtendedActivityVocabulary.Activity -> TODO()
|
||||
|
|
|
@ -80,7 +80,7 @@ class NoteQueryServiceImpl(private val postRepository: PostRepository, private v
|
|||
private suspend fun Query.toNote(): Note {
|
||||
return this.groupBy { it[Posts.id] }
|
||||
.map { it.value }
|
||||
.map { it.first().toNote(it.mapNotNull { it.toMediaOrNull() }) }
|
||||
.map { it.first().toNote(it.mapNotNull { resultRow -> resultRow.toMediaOrNull() }) }
|
||||
.singleOr { FailedToGetResourcesException("resource does not exist.") }
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ class APDeleteProcessor(
|
|||
val post = try {
|
||||
postQueryService.findByApId(deleteId)
|
||||
} catch (e: FailedToGetResourcesException) {
|
||||
logger.warn("FAILED delete id: {} is not found.", deleteId)
|
||||
logger.warn("FAILED delete id: {} is not found.", deleteId, e)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -37,15 +37,29 @@ class APRequestServiceImpl(
|
|||
logger.debug("START ActivityPub Request GET url: {}, signer: {}", url, signer?.url)
|
||||
val date = dateTimeFormatter.format(ZonedDateTime.now(ZoneId.of("GMT")))
|
||||
val u = URL(url)
|
||||
if (signer?.privateKey == null) {
|
||||
val bodyAsText = httpClient.get(url) {
|
||||
header("Accept", ContentType.Application.Activity)
|
||||
header("Date", date)
|
||||
}.bodyAsText()
|
||||
logBody(bodyAsText, url)
|
||||
return objectMapper.readValue(bodyAsText, responseClass)
|
||||
val httpResponse = if (signer?.privateKey == null) {
|
||||
apGetNotSign(url, date)
|
||||
} else {
|
||||
apGetSign(date, u, signer, url)
|
||||
}
|
||||
|
||||
val bodyAsText = httpResponse.bodyAsText()
|
||||
val readValue = objectMapper.readValue(bodyAsText, responseClass)
|
||||
logger.debug(
|
||||
"SUCCESS ActivityPub Request GET status: {} url: {}",
|
||||
httpResponse.status,
|
||||
httpResponse.request.url
|
||||
)
|
||||
logBody(bodyAsText, url)
|
||||
return readValue
|
||||
}
|
||||
|
||||
private suspend fun apGetSign(
|
||||
date: String,
|
||||
u: URL,
|
||||
signer: User,
|
||||
url: String
|
||||
): HttpResponse {
|
||||
val headers = headers {
|
||||
append("Accept", ContentType.Application.Activity)
|
||||
append("Date", date)
|
||||
|
@ -60,7 +74,7 @@ class APRequestServiceImpl(
|
|||
),
|
||||
privateKey = PrivateKey(
|
||||
keyId = "${signer.url}#pubkey",
|
||||
privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey),
|
||||
privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey!!),
|
||||
),
|
||||
signHeaders = listOf("(request-target)", "date", "host", "accept")
|
||||
)
|
||||
|
@ -75,15 +89,12 @@ class APRequestServiceImpl(
|
|||
}
|
||||
contentType(ContentType.Application.Activity)
|
||||
}
|
||||
val bodyAsText = httpResponse.bodyAsText()
|
||||
val readValue = objectMapper.readValue(bodyAsText, responseClass)
|
||||
logger.debug(
|
||||
"SUCCESS ActivityPub Request GET status: {} url: {}",
|
||||
httpResponse.status,
|
||||
httpResponse.request.url
|
||||
)
|
||||
logBody(bodyAsText, url)
|
||||
return readValue
|
||||
return httpResponse
|
||||
}
|
||||
|
||||
private suspend fun apGetNotSign(url: String, date: String?) = httpClient.get(url) {
|
||||
header("Accept", ContentType.Application.Activity)
|
||||
header("Date", date)
|
||||
}
|
||||
|
||||
override suspend fun <T : Object, R : Object> apPost(
|
||||
|
@ -96,18 +107,9 @@ class APRequestServiceImpl(
|
|||
return objectMapper.readValue(bodyAsText, responseClass)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override suspend fun <T : Object> apPost(url: String, body: T?, signer: User?): String {
|
||||
logger.debug("START ActivityPub Request POST url: {}, signer: {}", url, signer?.url)
|
||||
val requestBody = if (body != null) {
|
||||
val mutableListOf = mutableListOf<String>()
|
||||
mutableListOf.add("https://www.w3.org/ns/activitystreams")
|
||||
mutableListOf.addAll(body.context)
|
||||
body.context = mutableListOf
|
||||
objectMapper.writeValueAsString(body)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val requestBody = addContextIfNotNull(body)
|
||||
|
||||
logger.trace(
|
||||
"""
|
||||
|
@ -129,20 +131,44 @@ class APRequestServiceImpl(
|
|||
|
||||
val date = dateTimeFormatter.format(ZonedDateTime.now(ZoneId.of("GMT")))
|
||||
val u = URL(url)
|
||||
if (signer?.privateKey == null) {
|
||||
val bodyAsText = httpClient.post(url) {
|
||||
accept(ContentType.Application.Activity)
|
||||
header("Date", date)
|
||||
header("Digest", "sha-256=$digest")
|
||||
if (requestBody != null) {
|
||||
setBody(requestBody)
|
||||
contentType(ContentType.Application.Activity)
|
||||
}
|
||||
}.bodyAsText()
|
||||
logBody(bodyAsText, url)
|
||||
return bodyAsText
|
||||
val httpResponse = if (signer?.privateKey == null) {
|
||||
apPostNotSign(url, date, digest, requestBody)
|
||||
} else {
|
||||
apPostSign(date, u, digest, signer, requestBody)
|
||||
}
|
||||
|
||||
val bodyAsText = httpResponse.bodyAsText()
|
||||
logger.debug(
|
||||
"SUCCESS ActivityPub Request POST status: {} url: {}",
|
||||
httpResponse.status,
|
||||
httpResponse.request.url
|
||||
)
|
||||
logBody(bodyAsText, url)
|
||||
return bodyAsText
|
||||
}
|
||||
|
||||
private suspend fun apPostNotSign(
|
||||
url: String,
|
||||
date: String?,
|
||||
digest: String,
|
||||
requestBody: String?
|
||||
) = httpClient.post(url) {
|
||||
accept(ContentType.Application.Activity)
|
||||
header("Date", date)
|
||||
header("Digest", "sha-256=$digest")
|
||||
if (requestBody != null) {
|
||||
setBody(requestBody)
|
||||
contentType(ContentType.Application.Activity)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun apPostSign(
|
||||
date: String,
|
||||
u: URL,
|
||||
digest: String,
|
||||
signer: User,
|
||||
requestBody: String?
|
||||
): HttpResponse {
|
||||
val headers = headers {
|
||||
append("Accept", ContentType.Application.Activity)
|
||||
append("Date", date)
|
||||
|
@ -158,30 +184,31 @@ class APRequestServiceImpl(
|
|||
),
|
||||
privateKey = PrivateKey(
|
||||
keyId = signer.keyId,
|
||||
privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey)
|
||||
privateKey = RsaUtil.decodeRsaPrivateKeyPem(signer.privateKey!!)
|
||||
),
|
||||
signHeaders = listOf("(request-target)", "date", "host", "digest")
|
||||
)
|
||||
|
||||
val httpResponse = httpClient.post(url) {
|
||||
val httpResponse = httpClient.post(u) {
|
||||
headers {
|
||||
headers {
|
||||
appendAll(headers)
|
||||
append("Signature", sign.signatureHeader)
|
||||
remove("Host")
|
||||
}
|
||||
appendAll(headers)
|
||||
append("Signature", sign.signatureHeader)
|
||||
remove("Host")
|
||||
}
|
||||
setBody(requestBody)
|
||||
contentType(ContentType.Application.Activity)
|
||||
}
|
||||
val bodyAsText = httpResponse.bodyAsText()
|
||||
logger.debug(
|
||||
"SUCCESS ActivityPub Request POST status: {} url: {}",
|
||||
httpResponse.status,
|
||||
httpResponse.request.url
|
||||
)
|
||||
logBody(bodyAsText, url)
|
||||
return bodyAsText
|
||||
return httpResponse
|
||||
}
|
||||
|
||||
private fun <T : Object> addContextIfNotNull(body: T?) = if (body != null) {
|
||||
val mutableListOf = mutableListOf<String>()
|
||||
mutableListOf.add("https://www.w3.org/ns/activitystreams")
|
||||
mutableListOf.addAll(body.context)
|
||||
body.context = mutableListOf
|
||||
objectMapper.writeValueAsString(body)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
private fun logBody(bodyAsText: String, url: String) {
|
||||
|
|
|
@ -218,7 +218,6 @@ class APServiceImpl(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod", "NotImplementedDeclaration")
|
||||
override suspend fun processActivity(
|
||||
json: String,
|
||||
type: ActivityType,
|
||||
|
|
|
@ -86,6 +86,7 @@ class InboxJobProcessor(
|
|||
return verify.success
|
||||
}
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
private fun parseSignatureHeader(httpHeaders: HttpHeaders): Signature? {
|
||||
return try {
|
||||
signatureHeaderParser.parse(httpHeaders)
|
||||
|
|
|
@ -4,6 +4,7 @@ import dev.usbharu.hideout.activitypub.domain.model.Note
|
|||
import dev.usbharu.hideout.activitypub.query.NoteQueryService
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import dev.usbharu.hideout.core.query.FollowerQueryService
|
||||
import org.slf4j.LoggerFactory
|
||||
|
@ -28,20 +29,27 @@ class NoteApApiServiceImpl(
|
|||
}
|
||||
|
||||
Visibility.FOLLOWERS -> {
|
||||
if (userId == null) {
|
||||
return@transaction null
|
||||
}
|
||||
|
||||
if (followerQueryService.alreadyFollow(findById.second.userId, userId).not()) {
|
||||
return@transaction null
|
||||
}
|
||||
return@transaction findById.first
|
||||
return@transaction getFollowersNote(userId, findById)
|
||||
}
|
||||
|
||||
Visibility.DIRECT -> return@transaction null
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getFollowersNote(
|
||||
userId: Long?,
|
||||
findById: Pair<Note, Post>
|
||||
): Note? {
|
||||
if (userId == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (followerQueryService.alreadyFollow(findById.second.userId, userId)) {
|
||||
return findById.first
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(NoteApApiServiceImpl::class.java)
|
||||
}
|
||||
|
|
|
@ -77,69 +77,18 @@ class APUserServiceImpl(
|
|||
override suspend fun fetchPerson(url: String, targetActor: String?): Person =
|
||||
fetchPersonWithEntity(url, targetActor).first
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override suspend fun fetchPersonWithEntity(url: String, targetActor: String?): Pair<Person, User> {
|
||||
return try {
|
||||
val userEntity = userQueryService.findByUrl(url)
|
||||
val id = userEntity.url
|
||||
return Person(
|
||||
type = emptyList(),
|
||||
name = userEntity.name,
|
||||
id = id,
|
||||
preferredUsername = userEntity.name,
|
||||
summary = userEntity.description,
|
||||
inbox = "$id/inbox",
|
||||
outbox = "$id/outbox",
|
||||
url = id,
|
||||
icon = Image(
|
||||
type = emptyList(),
|
||||
name = "$id/icon.png",
|
||||
mediaType = "image/png",
|
||||
url = "$id/icon.png"
|
||||
),
|
||||
publicKey = Key(
|
||||
type = emptyList(),
|
||||
name = "Public Key",
|
||||
id = userEntity.keyId,
|
||||
owner = id,
|
||||
publicKeyPem = userEntity.publicKey
|
||||
),
|
||||
endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"),
|
||||
followers = userEntity.followers,
|
||||
following = userEntity.following
|
||||
) to userEntity
|
||||
return entityToPerson(userEntity, id) to userEntity
|
||||
} catch (ignore: FailedToGetResourcesException) {
|
||||
val person = apResourceResolveService.resolve<Person>(url, null as Long?)
|
||||
|
||||
val id = person.id ?: throw IllegalActivityPubObjectException("id is null")
|
||||
try {
|
||||
val userEntity = userQueryService.findByUrl(id)
|
||||
return Person(
|
||||
type = emptyList(),
|
||||
name = userEntity.name,
|
||||
id = id,
|
||||
preferredUsername = userEntity.name,
|
||||
summary = userEntity.description,
|
||||
inbox = "$id/inbox",
|
||||
outbox = "$id/outbox",
|
||||
url = id,
|
||||
icon = Image(
|
||||
type = emptyList(),
|
||||
name = "$id/icon.png",
|
||||
mediaType = "image/png",
|
||||
url = "$id/icon.png"
|
||||
),
|
||||
publicKey = Key(
|
||||
type = emptyList(),
|
||||
name = "Public Key",
|
||||
id = userEntity.keyId,
|
||||
owner = id,
|
||||
publicKeyPem = userEntity.publicKey
|
||||
),
|
||||
endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"),
|
||||
followers = userEntity.followers,
|
||||
following = userEntity.following
|
||||
) to userEntity
|
||||
return entityToPerson(userEntity, id) to userEntity
|
||||
} catch (_: FailedToGetResourcesException) {
|
||||
}
|
||||
person to userService.createRemoteUser(
|
||||
|
@ -163,4 +112,34 @@ class APUserServiceImpl(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun entityToPerson(
|
||||
userEntity: User,
|
||||
id: String
|
||||
) = Person(
|
||||
type = emptyList(),
|
||||
name = userEntity.name,
|
||||
id = id,
|
||||
preferredUsername = userEntity.name,
|
||||
summary = userEntity.description,
|
||||
inbox = "$id/inbox",
|
||||
outbox = "$id/outbox",
|
||||
url = id,
|
||||
icon = Image(
|
||||
type = emptyList(),
|
||||
name = "$id/icon.png",
|
||||
mediaType = "image/png",
|
||||
url = "$id/icon.png"
|
||||
),
|
||||
publicKey = Key(
|
||||
type = emptyList(),
|
||||
name = "Public Key",
|
||||
id = userEntity.keyId,
|
||||
owner = id,
|
||||
publicKeyPem = userEntity.publicKey
|
||||
),
|
||||
endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox"),
|
||||
followers = userEntity.followers,
|
||||
following = userEntity.following
|
||||
)
|
||||
}
|
||||
|
|
|
@ -200,8 +200,8 @@ class SecurityConfig {
|
|||
it.ignoringRequestMatchers(builder.pattern("/inbox"))
|
||||
it.ignoringRequestMatchers(PathRequest.toH2Console())
|
||||
}.headers {
|
||||
it.frameOptions {
|
||||
it.sameOrigin()
|
||||
it.frameOptions { frameOptionsConfig ->
|
||||
frameOptionsConfig.sameOrigin()
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
|
|
|
@ -2,6 +2,7 @@ package dev.usbharu.hideout.core.domain.model.media
|
|||
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.core.service.media.MimeType
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment
|
||||
|
||||
data class Media(
|
||||
val id: Long,
|
||||
|
@ -14,3 +15,19 @@ data class Media(
|
|||
val blurHash: String?,
|
||||
val description: String? = 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
|
||||
)
|
||||
|
|
|
@ -2,7 +2,6 @@ package dev.usbharu.hideout.core.domain.model.user
|
|||
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
@Repository
|
||||
interface UserRepository {
|
||||
suspend fun save(user: User): User
|
||||
|
|
|
@ -112,7 +112,9 @@ object DeliverRemoveReactionJob :
|
|||
val actor: Prop<DeliverRemoveReactionJob, String> = string("actor")
|
||||
val like: Prop<DeliverRemoveReactionJob, String> = string("like")
|
||||
|
||||
override fun convert(value: DeliverRemoveReactionJobParam): ScheduleContext<DeliverRemoveReactionJob>.(DeliverRemoveReactionJob) -> Unit =
|
||||
override fun convert(
|
||||
value: DeliverRemoveReactionJobParam
|
||||
): ScheduleContext<DeliverRemoveReactionJob>.(DeliverRemoveReactionJob) -> Unit =
|
||||
{
|
||||
props[id] = value.id
|
||||
props[inbox] = value.inbox
|
||||
|
|
|
@ -15,7 +15,7 @@ class PostQueryMapper(private val postResultRowMapper: ResultRowMapper<Post>) :
|
|||
.map { it.value }
|
||||
.map {
|
||||
it.first().let(postResultRowMapper::map)
|
||||
.copy(mediaIds = it.mapNotNull { it.getOrNull(PostsMedia.mediaId) })
|
||||
.copy(mediaIds = it.mapNotNull { resultRow -> resultRow.getOrNull(PostsMedia.mediaId) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.springframework.stereotype.Service
|
|||
|
||||
@Service
|
||||
@ConditionalOnProperty(name = ["hideout.use-mongodb"], havingValue = "false", matchIfMissing = true)
|
||||
class KJobJobQueueParentService() : JobQueueParentService {
|
||||
class KJobJobQueueParentService : JobQueueParentService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
|
||||
|
|
|
@ -30,7 +30,8 @@ class KjobMongoJobQueueParentService(private val mongoClient: MongoClient) : Job
|
|||
}
|
||||
|
||||
override suspend fun <T, J : HideoutJob<T, J>> scheduleTypeSafe(job: J, jobProps: T) {
|
||||
TODO("Not yet implemented")
|
||||
val convert = job.convert(jobProps)
|
||||
kjob.schedule(job, convert)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
|
|
|
@ -9,7 +9,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
|||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
@Suppress("InjectDispatcher")
|
||||
@ConditionalOnProperty("hideout.use-mongodb", havingValue = "true", matchIfMissing = false)
|
||||
class MongoTimelineRepositoryWrapper(
|
||||
private val mongoTimelineRepository: MongoTimelineRepository,
|
||||
|
|
|
@ -197,7 +197,7 @@ class ExposedOAuth2AuthorizationService(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod", "CastToNullableType", "UNCHECKED_CAST")
|
||||
fun ResultRow.toAuthorization(): OAuth2Authorization {
|
||||
val registeredClientId = this[Authorization.registeredClientId]
|
||||
|
||||
|
@ -272,7 +272,6 @@ class ExposedOAuth2AuthorizationService(
|
|||
oidcIdTokenValue,
|
||||
oidcTokenIssuedAt,
|
||||
oidcTokenExpiresAt,
|
||||
@Suppress("CastToNullableType")
|
||||
oidcTokenMetadata.getValue(OAuth2Authorization.Token.CLAIMS_METADATA_NAME)
|
||||
as MutableMap<String, Any>?
|
||||
)
|
||||
|
|
|
@ -74,11 +74,7 @@ class ImageMediaProcessService(private val imageMediaProcessorConfiguration: Ima
|
|||
convertType,
|
||||
it
|
||||
)
|
||||
if (write) {
|
||||
tempThumbnailFile
|
||||
} else {
|
||||
null
|
||||
}
|
||||
tempThumbnailFile.takeIf { write }
|
||||
}
|
||||
} else {
|
||||
null
|
||||
|
|
|
@ -45,7 +45,7 @@ class ExposedGenerateTimelineService(private val statusQueryService: StatusQuery
|
|||
it[Timelines.postId],
|
||||
it[Timelines.replyId],
|
||||
it[Timelines.repostId],
|
||||
it[Timelines.mediaIds].split(",").mapNotNull { it.toLongOrNull() }
|
||||
it[Timelines.mediaIds].split(",").mapNotNull { s -> s.toLongOrNull() }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package dev.usbharu.hideout.core.service.user
|
|||
import dev.usbharu.hideout.core.domain.model.user.User
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
@Service
|
||||
interface UserService {
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ class UserServiceImpl(
|
|||
}
|
||||
|
||||
override suspend fun createRemoteUser(user: RemoteUserCreateDto): User {
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
val instance = try {
|
||||
instanceService.fetchInstance(user.url, user.sharedInbox)
|
||||
} catch (e: Exception) {
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
package dev.usbharu.hideout.mastodon.infrastructure.exposedquery
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.*
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Account
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Status
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery
|
||||
import dev.usbharu.hideout.mastodon.query.StatusQueryService
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.jetbrains.exposed.sql.innerJoin
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.time.Instant
|
||||
|
||||
@Repository
|
||||
class StatusQueryServiceImpl : StatusQueryService {
|
||||
@Suppress("LongMethod")
|
||||
override suspend fun findByPostIds(ids: List<Long>): List<Status> = findByPostIdsWithMediaAttachments(ids)
|
||||
override suspend fun findByPostIds(ids: List<Long>): List<Status> = findByPostIdsWithMedia(ids)
|
||||
|
||||
override suspend fun findByPostIdsWithMediaIds(statusQueries: List<StatusQuery>): List<Status> {
|
||||
val postIdSet = mutableSetOf<Long>()
|
||||
|
@ -29,23 +26,7 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
.associate { it[Posts.id] to toStatus(it) }
|
||||
val mediaMap = Media.select { Media.id inList mediaIdSet }
|
||||
.associate {
|
||||
it[Media.id] to it.toMedia().let {
|
||||
MediaAttachment(
|
||||
id = it.id.toString(),
|
||||
type = when (it.type) {
|
||||
FileType.Image -> MediaAttachment.Type.image
|
||||
FileType.Video -> MediaAttachment.Type.video
|
||||
FileType.Audio -> MediaAttachment.Type.audio
|
||||
FileType.Unknown -> MediaAttachment.Type.unknown
|
||||
},
|
||||
url = it.url,
|
||||
previewUrl = it.thumbnailUrl,
|
||||
remoteUrl = it.remoteUrl,
|
||||
description = "",
|
||||
blurhash = it.blurHash,
|
||||
textUrl = it.url
|
||||
)
|
||||
}
|
||||
it[Media.id] to it.toMedia().toMediaAttachments()
|
||||
}
|
||||
|
||||
return statusQueries.mapNotNull { statusQuery ->
|
||||
|
@ -58,18 +39,6 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private suspend fun internalFindByPostIds(ids: List<Long>): List<Status> {
|
||||
val pairs = Posts
|
||||
.innerJoin(Users, onColumn = { Posts.userId }, otherColumn = { Users.id })
|
||||
.select { Posts.id inList ids }
|
||||
.map {
|
||||
toStatus(it) to it[Posts.repostId]
|
||||
}
|
||||
|
||||
return resolveReplyAndRepost(pairs)
|
||||
}
|
||||
|
||||
private fun resolveReplyAndRepost(pairs: List<Pair<Status, Long?>>): List<Status> {
|
||||
val statuses = pairs.map { it.first }
|
||||
return pairs
|
||||
|
@ -89,8 +58,7 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("FunctionMaxLength")
|
||||
private suspend fun findByPostIdsWithMediaAttachments(ids: List<Long>): List<Status> {
|
||||
private suspend fun findByPostIdsWithMedia(ids: List<Long>): List<Status> {
|
||||
val pairs = Posts
|
||||
.leftJoin(PostsMedia)
|
||||
.leftJoin(Users)
|
||||
|
@ -100,24 +68,8 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
.map { it.value }
|
||||
.map {
|
||||
toStatus(it.first()).copy(
|
||||
mediaAttachments = it.mapNotNull {
|
||||
it.toMediaOrNull()?.let {
|
||||
MediaAttachment(
|
||||
id = it.id.toString(),
|
||||
type = when (it.type) {
|
||||
FileType.Image -> MediaAttachment.Type.image
|
||||
FileType.Video -> MediaAttachment.Type.video
|
||||
FileType.Audio -> MediaAttachment.Type.audio
|
||||
FileType.Unknown -> MediaAttachment.Type.unknown
|
||||
},
|
||||
url = it.url,
|
||||
previewUrl = it.thumbnailUrl,
|
||||
remoteUrl = it.remoteUrl,
|
||||
description = "",
|
||||
blurhash = it.blurHash,
|
||||
textUrl = it.url
|
||||
)
|
||||
}
|
||||
mediaAttachments = it.mapNotNull { resultRow ->
|
||||
resultRow.toMediaOrNull()?.toMediaAttachments()
|
||||
}
|
||||
) to it.first()[Posts.repostId]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package dev.usbharu.hideout.mastodon.interfaces.api.status
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Status
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequestPoll
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest.Visibility.*
|
||||
|
||||
@Suppress("VariableNaming", "EnumEntryName")
|
||||
class StatusesRequest {
|
||||
|
@ -67,7 +70,7 @@ class StatusesRequest {
|
|||
" scheduledAt=$scheduled_at)"
|
||||
}
|
||||
|
||||
@Suppress("EnumNaming")
|
||||
@Suppress("EnumNaming", "EnumEntryNameCase")
|
||||
enum class Visibility {
|
||||
`public`,
|
||||
unlisted,
|
||||
|
@ -75,3 +78,23 @@ class StatusesRequest {
|
|||
direct
|
||||
}
|
||||
}
|
||||
|
||||
fun StatusesRequest.Visibility?.toPostVisibility(): Visibility {
|
||||
return when (this) {
|
||||
public -> Visibility.PUBLIC
|
||||
unlisted -> Visibility.UNLISTED
|
||||
private -> Visibility.FOLLOWERS
|
||||
direct -> Visibility.DIRECT
|
||||
null -> Visibility.PUBLIC
|
||||
}
|
||||
}
|
||||
|
||||
fun StatusesRequest.Visibility?.toStatusVisibility(): Status.Visibility {
|
||||
return when (this) {
|
||||
public -> Status.Visibility.public
|
||||
unlisted -> Status.Visibility.unlisted
|
||||
private -> Status.Visibility.private
|
||||
direct -> Status.Visibility.direct
|
||||
null -> Status.Visibility.public
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,15 @@ package dev.usbharu.hideout.mastodon.service.status
|
|||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments
|
||||
import dev.usbharu.hideout.core.query.PostQueryService
|
||||
import dev.usbharu.hideout.core.query.UserQueryService
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.core.service.post.PostCreateDto
|
||||
import dev.usbharu.hideout.core.service.post.PostService
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Status
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusesRequest
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.status.toPostVisibility
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.status.toStatusVisibility
|
||||
import dev.usbharu.hideout.mastodon.service.account.AccountService
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.Instant
|
||||
|
@ -34,24 +34,15 @@ class StatsesApiServiceImpl(
|
|||
private val transaction: Transaction
|
||||
) :
|
||||
StatusesApiService {
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
override suspend fun postStatus(
|
||||
statusesRequest: StatusesRequest,
|
||||
userId: Long
|
||||
): Status = transaction.transaction {
|
||||
val visibility = when (statusesRequest.visibility) {
|
||||
StatusesRequest.Visibility.public -> Visibility.PUBLIC
|
||||
StatusesRequest.Visibility.unlisted -> Visibility.UNLISTED
|
||||
StatusesRequest.Visibility.private -> Visibility.FOLLOWERS
|
||||
StatusesRequest.Visibility.direct -> Visibility.DIRECT
|
||||
null -> Visibility.PUBLIC
|
||||
}
|
||||
|
||||
val post = postService.createLocal(
|
||||
PostCreateDto(
|
||||
text = statusesRequest.status.orEmpty(),
|
||||
overview = statusesRequest.spoiler_text,
|
||||
visibility = visibility,
|
||||
visibility = statusesRequest.visibility.toPostVisibility(),
|
||||
repolyId = statusesRequest.in_reply_to_id?.toLongOrNull(),
|
||||
userId = userId,
|
||||
mediaIds = statusesRequest.media_ids.map { it.toLong() }
|
||||
|
@ -59,14 +50,6 @@ class StatsesApiServiceImpl(
|
|||
)
|
||||
val account = accountService.findById(userId)
|
||||
|
||||
val postVisibility = when (statusesRequest.visibility) {
|
||||
StatusesRequest.Visibility.public -> Status.Visibility.public
|
||||
StatusesRequest.Visibility.unlisted -> Status.Visibility.unlisted
|
||||
StatusesRequest.Visibility.private -> Status.Visibility.private
|
||||
StatusesRequest.Visibility.direct -> Status.Visibility.direct
|
||||
null -> Status.Visibility.public
|
||||
}
|
||||
|
||||
val replyUser = if (post.replyId != null) {
|
||||
try {
|
||||
userQueryService.findById(postQueryService.findById(post.replyId).userId).id
|
||||
|
@ -81,21 +64,7 @@ class StatsesApiServiceImpl(
|
|||
val mediaAttachment = post.mediaIds.map { mediaId ->
|
||||
mediaRepository.findById(mediaId)
|
||||
}.map {
|
||||
MediaAttachment(
|
||||
id = it.id.toString(),
|
||||
type = when (it.type) {
|
||||
FileType.Image -> MediaAttachment.Type.image
|
||||
FileType.Video -> MediaAttachment.Type.video
|
||||
FileType.Audio -> MediaAttachment.Type.audio
|
||||
FileType.Unknown -> MediaAttachment.Type.unknown
|
||||
},
|
||||
url = it.url,
|
||||
previewUrl = it.thumbnailUrl,
|
||||
remoteUrl = it.remoteUrl,
|
||||
description = "",
|
||||
blurhash = it.blurHash,
|
||||
textUrl = it.url
|
||||
)
|
||||
it.toMediaAttachments()
|
||||
}
|
||||
|
||||
Status(
|
||||
|
@ -104,7 +73,7 @@ class StatsesApiServiceImpl(
|
|||
createdAt = Instant.ofEpochMilli(post.createdAt).toString(),
|
||||
account = account,
|
||||
content = post.text,
|
||||
visibility = postVisibility,
|
||||
visibility = statusesRequest.visibility.toStatusVisibility(),
|
||||
sensitive = post.sensitive,
|
||||
spoilerText = post.overview.orEmpty(),
|
||||
mediaAttachments = mediaAttachment,
|
||||
|
|
Loading…
Reference in New Issue