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