mirror of https://github.com/usbharu/Hideout.git
feat: トランザクションエラーがある程度発生しないように
This commit is contained in:
parent
ee8bbf5091
commit
37db201f87
|
@ -20,6 +20,7 @@ import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
|||
import dev.usbharu.hideout.service.api.IPostApiService
|
||||
import dev.usbharu.hideout.service.api.IUserApiService
|
||||
import dev.usbharu.hideout.service.api.UserAuthApiService
|
||||
import dev.usbharu.hideout.service.api.WebFingerApiService
|
||||
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
|
||||
import dev.usbharu.hideout.service.core.*
|
||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||
|
@ -75,7 +76,7 @@ fun Application.parent() {
|
|||
level = LogLevel.INFO
|
||||
}
|
||||
install(httpSignaturePlugin) {
|
||||
keyMap = KtorKeyMap(get())
|
||||
keyMap = KtorKeyMap(get(), get())
|
||||
}
|
||||
expectSuccess = true
|
||||
}
|
||||
|
@ -116,6 +117,7 @@ fun Application.parent() {
|
|||
userQueryService = inject<UserQueryService>().value,
|
||||
followerQueryService = inject<FollowerQueryService>().value,
|
||||
userAuthApiService = inject<UserAuthApiService>().value,
|
||||
webFingerApiService = inject<WebFingerApiService>().value,
|
||||
transaction = inject<Transaction>().value
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package dev.usbharu.hideout.plugins
|
|||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.domain.model.ap.JsonLd
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import dev.usbharu.hideout.service.user.UserAuthService
|
||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||
import io.ktor.client.*
|
||||
|
@ -164,19 +165,21 @@ val httpSignaturePlugin = createClientPlugin("HttpSign", ::HttpSignaturePluginCo
|
|||
}
|
||||
}
|
||||
|
||||
class KtorKeyMap(private val userQueryService: UserQueryService) : KeyMap {
|
||||
class KtorKeyMap(private val userQueryService: UserQueryService, private val transaction: Transaction) : KeyMap {
|
||||
override fun getPublicKey(keyId: String?): PublicKey = runBlocking {
|
||||
val username = (keyId ?: throw IllegalArgumentException("keyId is null")).substringBeforeLast("#pubkey")
|
||||
.substringAfterLast("/")
|
||||
val publicBytes = Base64.getDecoder().decode(
|
||||
userQueryService.findByNameAndDomain(
|
||||
username,
|
||||
Config.configData.domain
|
||||
).run {
|
||||
publicKey
|
||||
.replace("-----BEGIN PUBLIC KEY-----", "")
|
||||
.replace("-----END PUBLIC KEY-----", "")
|
||||
.replace("\n", "")
|
||||
transaction.transaction {
|
||||
userQueryService.findByNameAndDomain(
|
||||
username,
|
||||
Config.configData.domain
|
||||
).run {
|
||||
publicKey
|
||||
.replace("-----BEGIN PUBLIC KEY-----", "")
|
||||
.replace("-----END PUBLIC KEY-----", "")
|
||||
.replace("\n", "")
|
||||
}
|
||||
}
|
||||
)
|
||||
val x509EncodedKeySpec = X509EncodedKeySpec(publicBytes)
|
||||
|
@ -187,13 +190,15 @@ class KtorKeyMap(private val userQueryService: UserQueryService) : KeyMap {
|
|||
val username = (keyId ?: throw IllegalArgumentException("keyId is null")).substringBeforeLast("#pubkey")
|
||||
.substringAfterLast("/")
|
||||
val publicBytes = Base64.getDecoder().decode(
|
||||
userQueryService.findByNameAndDomain(
|
||||
username,
|
||||
Config.configData.domain
|
||||
).privateKey?.run {
|
||||
replace("-----BEGIN PRIVATE KEY-----", "")
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
.replace("\n", "")
|
||||
transaction.transaction {
|
||||
userQueryService.findByNameAndDomain(
|
||||
username,
|
||||
Config.configData.domain
|
||||
).privateKey?.run {
|
||||
replace("-----BEGIN PRIVATE KEY-----", "")
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
.replace("\n", "")
|
||||
}
|
||||
}
|
||||
)
|
||||
val x509EncodedKeySpec = PKCS8EncodedKeySpec(publicBytes)
|
||||
|
|
|
@ -14,6 +14,7 @@ import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
|||
import dev.usbharu.hideout.service.api.IPostApiService
|
||||
import dev.usbharu.hideout.service.api.IUserApiService
|
||||
import dev.usbharu.hideout.service.api.UserAuthApiService
|
||||
import dev.usbharu.hideout.service.api.WebFingerApiService
|
||||
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import dev.usbharu.hideout.service.user.IUserService
|
||||
|
@ -32,6 +33,7 @@ fun Application.configureRouting(
|
|||
userQueryService: UserQueryService,
|
||||
followerQueryService: FollowerQueryService,
|
||||
userAuthApiService: UserAuthApiService,
|
||||
webFingerApiService: WebFingerApiService,
|
||||
transaction: Transaction
|
||||
) {
|
||||
install(AutoHeadResponse)
|
||||
|
@ -39,7 +41,7 @@ fun Application.configureRouting(
|
|||
inbox(httpSignatureVerifyService, activityPubService)
|
||||
outbox()
|
||||
usersAP(activityPubUserService, userQueryService, followerQueryService, transaction)
|
||||
webfinger(userQueryService)
|
||||
webfinger(webFingerApiService)
|
||||
route("/api/internal/v1") {
|
||||
posts(postService)
|
||||
users(userService, userApiService)
|
||||
|
|
|
@ -4,14 +4,14 @@ import dev.usbharu.hideout.config.Config
|
|||
import dev.usbharu.hideout.domain.model.wellknown.WebFinger
|
||||
import dev.usbharu.hideout.exception.IllegalParameterException
|
||||
import dev.usbharu.hideout.exception.ParameterNotExistException
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.api.WebFingerApiService
|
||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Routing.webfinger(userQueryService: UserQueryService) {
|
||||
fun Routing.webfinger(webFingerApiService: WebFingerApiService) {
|
||||
route("/.well-known/webfinger") {
|
||||
get {
|
||||
val acct = call.request.queryParameters["resource"]?.decodeURLPart()
|
||||
|
@ -25,7 +25,7 @@ fun Routing.webfinger(userQueryService: UserQueryService) {
|
|||
.substringAfter("acct:")
|
||||
.trimStart('@')
|
||||
|
||||
val userEntity = userQueryService.findByNameAndDomain(accountName, Config.configData.domain)
|
||||
val userEntity = webFingerApiService.findByNameAndDomain(accountName, Config.configData.domain)
|
||||
|
||||
val webFinger = WebFinger(
|
||||
subject = acct,
|
||||
|
|
|
@ -5,12 +5,14 @@ import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
|
|||
import dev.usbharu.hideout.domain.model.ap.Create
|
||||
import dev.usbharu.hideout.domain.model.ap.Note
|
||||
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import io.ktor.http.*
|
||||
import org.koin.core.annotation.Single
|
||||
|
||||
@Single
|
||||
class ActivityPubCreateServiceImpl(
|
||||
private val activityPubNoteService: ActivityPubNoteService
|
||||
private val activityPubNoteService: ActivityPubNoteService,
|
||||
private val transaction: Transaction
|
||||
) : ActivityPubCreateService {
|
||||
override suspend fun receiveCreate(create: Create): ActivityPubResponse {
|
||||
val value = create.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
||||
|
@ -18,8 +20,10 @@ class ActivityPubCreateServiceImpl(
|
|||
throw IllegalActivityPubObjectException("object is not Note")
|
||||
}
|
||||
|
||||
val note = value as Note
|
||||
activityPubNoteService.fetchNote(note)
|
||||
return ActivityPubStringResponse(HttpStatusCode.OK, "Created")
|
||||
return transaction.transaction {
|
||||
val note = value as Note
|
||||
activityPubNoteService.fetchNote(note)
|
||||
ActivityPubStringResponse(HttpStatusCode.OK, "Created")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import dev.usbharu.hideout.domain.model.ap.Like
|
|||
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
||||
import dev.usbharu.hideout.query.PostQueryService
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import dev.usbharu.hideout.service.reaction.IReactionService
|
||||
import io.ktor.http.*
|
||||
import org.koin.core.annotation.Single
|
||||
|
@ -16,23 +17,26 @@ class ActivityPubLikeServiceImpl(
|
|||
private val activityPubUserService: ActivityPubUserService,
|
||||
private val activityPubNoteService: ActivityPubNoteService,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val postQueryService: PostQueryService
|
||||
private val postQueryService: PostQueryService,
|
||||
private val transaction: Transaction
|
||||
) : ActivityPubLikeService {
|
||||
override suspend fun receiveLike(like: Like): ActivityPubResponse {
|
||||
val actor = like.actor ?: throw IllegalActivityPubObjectException("actor is null")
|
||||
val content = like.content ?: throw IllegalActivityPubObjectException("content is null")
|
||||
like.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
||||
val person = activityPubUserService.fetchPerson(actor)
|
||||
activityPubNoteService.fetchNote(like.`object`!!)
|
||||
transaction.transaction {
|
||||
val person = activityPubUserService.fetchPerson(actor)
|
||||
activityPubNoteService.fetchNote(like.`object`!!)
|
||||
|
||||
val user = userQueryService.findByUrl(
|
||||
person.url
|
||||
?: throw IllegalActivityPubObjectException("actor is not found")
|
||||
)
|
||||
val user = userQueryService.findByUrl(
|
||||
person.url
|
||||
?: throw IllegalActivityPubObjectException("actor is not found")
|
||||
)
|
||||
|
||||
val post = postQueryService.findByUrl(like.`object`!!)
|
||||
val post = postQueryService.findByUrl(like.`object`!!)
|
||||
|
||||
reactionService.receiveReaction(content, actor.substringAfter("://").substringBefore("/"), user.id, post.id)
|
||||
reactionService.receiveReaction(content, actor.substringAfter("://").substringBefore("/"), user.id, post.id)
|
||||
}
|
||||
return ActivityPubStringResponse(HttpStatusCode.OK, "")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import dev.usbharu.hideout.domain.model.ap.Follow
|
|||
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
||||
import dev.usbharu.hideout.plugins.postAp
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||
import dev.usbharu.hideout.service.user.IUserService
|
||||
import io.ktor.client.*
|
||||
|
@ -22,7 +23,8 @@ class ActivityPubReceiveFollowServiceImpl(
|
|||
private val activityPubUserService: ActivityPubUserService,
|
||||
private val userService: IUserService,
|
||||
private val httpClient: HttpClient,
|
||||
private val userQueryService: UserQueryService
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction
|
||||
) : ActivityPubReceiveFollowService {
|
||||
override suspend fun receiveFollow(follow: Follow): ActivityPubResponse {
|
||||
// TODO: Verify HTTP Signature
|
||||
|
@ -35,24 +37,26 @@ class ActivityPubReceiveFollowServiceImpl(
|
|||
}
|
||||
|
||||
override suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>) {
|
||||
val actor = props[ReceiveFollowJob.actor]
|
||||
val targetActor = props[ReceiveFollowJob.targetActor]
|
||||
val person = activityPubUserService.fetchPerson(actor, targetActor)
|
||||
val follow = Config.configData.objectMapper.readValue<Follow>(props[ReceiveFollowJob.follow])
|
||||
httpClient.postAp(
|
||||
urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found"),
|
||||
username = "$targetActor#pubkey",
|
||||
jsonLd = Accept(
|
||||
name = "Follow",
|
||||
`object` = follow,
|
||||
actor = targetActor
|
||||
transaction.transaction {
|
||||
val actor = props[ReceiveFollowJob.actor]
|
||||
val targetActor = props[ReceiveFollowJob.targetActor]
|
||||
val person = activityPubUserService.fetchPerson(actor, targetActor)
|
||||
val follow = Config.configData.objectMapper.readValue<Follow>(props[ReceiveFollowJob.follow])
|
||||
httpClient.postAp(
|
||||
urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found"),
|
||||
username = "$targetActor#pubkey",
|
||||
jsonLd = Accept(
|
||||
name = "Follow",
|
||||
`object` = follow,
|
||||
actor = targetActor
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val targetEntity = userQueryService.findByUrl(targetActor)
|
||||
val followActorEntity =
|
||||
userQueryService.findByUrl(follow.actor ?: throw java.lang.IllegalArgumentException("Actor is null"))
|
||||
val targetEntity = userQueryService.findByUrl(targetActor)
|
||||
val followActorEntity =
|
||||
userQueryService.findByUrl(follow.actor ?: throw java.lang.IllegalArgumentException("Actor is null"))
|
||||
|
||||
userService.followRequest(targetEntity.id, followActorEntity.id)
|
||||
userService.followRequest(targetEntity.id, followActorEntity.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
|
|||
import dev.usbharu.hideout.domain.model.ap.Follow
|
||||
import dev.usbharu.hideout.domain.model.ap.Undo
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import dev.usbharu.hideout.service.user.IUserService
|
||||
import io.ktor.http.*
|
||||
import org.koin.core.annotation.Single
|
||||
|
@ -14,7 +15,8 @@ import org.koin.core.annotation.Single
|
|||
class ActivityPubUndoServiceImpl(
|
||||
private val userService: IUserService,
|
||||
private val activityPubUserService: ActivityPubUserService,
|
||||
private val userQueryService: UserQueryService
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction
|
||||
) : ActivityPubUndoService {
|
||||
override suspend fun receiveUndo(undo: Undo): ActivityPubResponse {
|
||||
if (undo.actor == null) {
|
||||
|
@ -33,11 +35,12 @@ class ActivityPubUndoServiceImpl(
|
|||
if (follow.`object` == null) {
|
||||
return ActivityPubStringResponse(HttpStatusCode.BadRequest, "object.object is null")
|
||||
}
|
||||
|
||||
activityPubUserService.fetchPerson(undo.actor!!, follow.`object`)
|
||||
val follower = userQueryService.findByUrl(undo.actor!!)
|
||||
val target = userQueryService.findByUrl(follow.`object`!!)
|
||||
userService.unfollow(target.id, follower.id)
|
||||
transaction.transaction {
|
||||
activityPubUserService.fetchPerson(undo.actor!!, follow.`object`)
|
||||
val follower = userQueryService.findByUrl(undo.actor!!)
|
||||
val target = userQueryService.findByUrl(follow.`object`!!)
|
||||
userService.unfollow(target.id, follower.id)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
|
|
|
@ -6,10 +6,10 @@ import dev.usbharu.hideout.domain.model.ap.Image
|
|||
import dev.usbharu.hideout.domain.model.ap.Key
|
||||
import dev.usbharu.hideout.domain.model.ap.Person
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
|
||||
import dev.usbharu.hideout.exception.UserNotFoundException
|
||||
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
||||
import dev.usbharu.hideout.plugins.getAp
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import dev.usbharu.hideout.service.user.IUserService
|
||||
import dev.usbharu.hideout.util.HttpUtil.Activity
|
||||
import io.ktor.client.*
|
||||
|
@ -22,13 +22,16 @@ import org.koin.core.annotation.Single
|
|||
class ActivityPubUserServiceImpl(
|
||||
private val userService: IUserService,
|
||||
private val httpClient: HttpClient,
|
||||
private val userQueryService: UserQueryService
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction
|
||||
) :
|
||||
ActivityPubUserService {
|
||||
|
||||
override suspend fun getPersonByName(name: String): Person {
|
||||
val userEntity = transaction.transaction {
|
||||
userQueryService.findByNameAndDomain(name, Config.configData.domain)
|
||||
}
|
||||
// TODO: JOINで書き直し
|
||||
val userEntity = userQueryService.findByNameAndDomain(name, Config.configData.domain)
|
||||
val userUrl = "${Config.configData.url}/users/$name"
|
||||
return Person(
|
||||
type = emptyList(),
|
||||
|
@ -81,7 +84,7 @@ class ActivityPubUserServiceImpl(
|
|||
publicKeyPem = userEntity.publicKey
|
||||
)
|
||||
)
|
||||
} catch (ignore: UserNotFoundException) {
|
||||
} catch (ignore: NoSuchElementException) {
|
||||
val httpResponse = if (targetActor != null) {
|
||||
httpClient.getAp(url, "$targetActor#pubkey")
|
||||
} else {
|
||||
|
@ -108,5 +111,6 @@ class ActivityPubUserServiceImpl(
|
|||
)
|
||||
person
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package dev.usbharu.hideout.service.api
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||
|
||||
interface WebFingerApiService {
|
||||
suspend fun findByNameAndDomain(name: String, domain: String): User
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package dev.usbharu.hideout.service.api
|
||||
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import org.koin.core.annotation.Single
|
||||
|
||||
@Single
|
||||
class WebFingerApiServiceImpl(private val transaction: Transaction, private val userQueryService: UserQueryService) :
|
||||
WebFingerApiService {
|
||||
override suspend fun findByNameAndDomain(name: String, domain: String): User {
|
||||
return transaction.transaction {
|
||||
userQueryService.findByNameAndDomain(name, domain)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,14 +2,18 @@ package dev.usbharu.hideout.service.auth
|
|||
|
||||
import dev.usbharu.hideout.plugins.KtorKeyMap
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import io.ktor.http.*
|
||||
import org.koin.core.annotation.Single
|
||||
import tech.barbero.http.message.signing.SignatureHeaderVerifier
|
||||
|
||||
@Single
|
||||
class HttpSignatureVerifyServiceImpl(private val userQueryService: UserQueryService) : HttpSignatureVerifyService {
|
||||
class HttpSignatureVerifyServiceImpl(
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction
|
||||
) : HttpSignatureVerifyService {
|
||||
override fun verify(headers: Headers): Boolean {
|
||||
val build = SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userQueryService)).build()
|
||||
val build = SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userQueryService, transaction)).build()
|
||||
return true
|
||||
// build.verify(object : HttpMessage {
|
||||
// override fun headerValues(name: String?): MutableList<String> {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="TRACE">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<logger name="org.eclipse.jetty" level="INFO"/>
|
||||
|
|
Loading…
Reference in New Issue