mirror of https://github.com/usbharu/Hideout.git
fix: ジョブキュー等を修正
This commit is contained in:
parent
a0ffd3cfe1
commit
3c310952b5
|
@ -0,0 +1,49 @@
|
|||
package dev.usbharu.hideout
|
||||
|
||||
import dev.usbharu.hideout.domain.model.job.HideoutJob
|
||||
import dev.usbharu.hideout.service.ap.APService
|
||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||
import dev.usbharu.hideout.service.job.JobQueueWorkerService
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.boot.ApplicationArguments
|
||||
import org.springframework.boot.ApplicationRunner
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class JobQueueRunner(private val jobQueueParentService: JobQueueParentService, private val jobs: List<HideoutJob>) :
|
||||
ApplicationRunner {
|
||||
override fun run(args: ApplicationArguments?) {
|
||||
LOGGER.info("Init job queue. ${jobs.size}")
|
||||
jobQueueParentService.init(jobs)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val LOGGER = LoggerFactory.getLogger(JobQueueRunner::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
class JobQueueWorkerRunner(
|
||||
private val jobQueueWorkerService: JobQueueWorkerService,
|
||||
private val jobs: List<HideoutJob>,
|
||||
private val apService: APService
|
||||
) : ApplicationRunner {
|
||||
override fun run(args: ApplicationArguments?) {
|
||||
LOGGER.info("Init job queue worker.")
|
||||
jobQueueWorkerService.init(jobs.map {
|
||||
it to {
|
||||
execute {
|
||||
LOGGER.debug("excute job ${it.name}")
|
||||
apService.processActivity(
|
||||
job = this,
|
||||
hideoutJob = it
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
val LOGGER = LoggerFactory.getLogger(JobQueueWorkerRunner::class.java)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package dev.usbharu.hideout.config
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Configuration
|
||||
class ActivityPubConfig {
|
||||
|
||||
@Bean
|
||||
@Qualifier("activitypub")
|
||||
fun objectMapper(): ObjectMapper {
|
||||
val objectMapper = jacksonObjectMapper()
|
||||
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
return objectMapper
|
||||
}
|
||||
}
|
|
@ -1,12 +1,38 @@
|
|||
package dev.usbharu.hideout.config
|
||||
|
||||
import dev.usbharu.hideout.plugins.KtorKeyMap
|
||||
import dev.usbharu.hideout.plugins.httpSignaturePlugin
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.cio.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import tech.barbero.http.message.signing.KeyMap
|
||||
|
||||
@Configuration
|
||||
class HttpClientConfig {
|
||||
@Bean
|
||||
fun httpClient(): HttpClient = HttpClient(CIO)
|
||||
fun httpClient(keyMap: KeyMap): HttpClient = HttpClient(CIO).config {
|
||||
install(httpSignaturePlugin) {
|
||||
this.keyMap = keyMap
|
||||
}
|
||||
install(Logging) {
|
||||
logger = Logger.DEFAULT
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
expectSuccess = true
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun keyMap(
|
||||
userQueryService: UserQueryService,
|
||||
transaction: Transaction,
|
||||
applicationConfig: ApplicationConfig
|
||||
): KtorKeyMap {
|
||||
return KtorKeyMap(
|
||||
userQueryService, transaction, applicationConfig
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import java.security.interfaces.RSAPrivateKey
|
|||
import java.security.interfaces.RSAPublicKey
|
||||
import java.util.*
|
||||
|
||||
@EnableWebSecurity(debug = true)
|
||||
@EnableWebSecurity(debug = false)
|
||||
@Configuration
|
||||
class SecurityConfig {
|
||||
|
||||
|
@ -66,6 +66,7 @@ class SecurityConfig {
|
|||
it.requestMatchers(PathRequest.toH2Console()).permitAll()
|
||||
it.requestMatchers(
|
||||
builder.pattern("/inbox"),
|
||||
builder.pattern("/users/*/inbox"),
|
||||
builder.pattern("/api/v1/apps"),
|
||||
builder.pattern("/api/v1/instance/**"),
|
||||
builder.pattern("/.well-known/**"),
|
||||
|
@ -85,6 +86,8 @@ class SecurityConfig {
|
|||
.formLogin(Customizer.withDefaults())
|
||||
.csrf {
|
||||
it.ignoringRequestMatchers(builder.pattern("/api/**"))
|
||||
it.ignoringRequestMatchers(builder.pattern("/users/*/inbox"))
|
||||
it.ignoringRequestMatchers(builder.pattern("/inbox"))
|
||||
it.ignoringRequestMatchers(PathRequest.toH2Console())
|
||||
}
|
||||
.headers {
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired
|
|||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import java.net.URL
|
||||
|
||||
@Configuration
|
||||
class SpringConfig {
|
||||
|
@ -28,7 +29,7 @@ class SpringConfig {
|
|||
|
||||
@ConfigurationProperties("hideout")
|
||||
data class ApplicationConfig(
|
||||
val url: String
|
||||
val url: URL
|
||||
)
|
||||
|
||||
@ConfigurationProperties("hideout.database")
|
||||
|
|
|
@ -15,7 +15,7 @@ interface InboxController {
|
|||
produces = ["application/activity+json", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""],
|
||||
method = [RequestMethod.GET, RequestMethod.POST]
|
||||
)
|
||||
suspend fun inbox(@RequestBody string: String): ResponseEntity<Unit> {
|
||||
fun inbox(@RequestBody string: String): ResponseEntity<Unit> {
|
||||
return ResponseEntity(HttpStatus.ACCEPTED)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package dev.usbharu.hideout.controller
|
||||
|
||||
import dev.usbharu.hideout.service.ap.APService
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
|
@ -8,9 +9,9 @@ import org.springframework.web.bind.annotation.RestController
|
|||
|
||||
@RestController
|
||||
class InboxControllerImpl(private val apService: APService) : InboxController {
|
||||
override suspend fun inbox(@RequestBody string: String): ResponseEntity<Unit> {
|
||||
override fun inbox(@RequestBody string: String): ResponseEntity<Unit> = runBlocking {
|
||||
val parseActivity = apService.parseActivity(string)
|
||||
apService.processActivity(string, parseActivity)
|
||||
return ResponseEntity(HttpStatus.ACCEPTED)
|
||||
ResponseEntity(HttpStatus.ACCEPTED)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,5 +9,5 @@ import org.springframework.web.bind.annotation.RestController
|
|||
@RestController
|
||||
interface UserAPController {
|
||||
@GetMapping("/users/{username}")
|
||||
suspend fun userAp(@PathVariable("username") username: String): ResponseEntity<Person>
|
||||
fun userAp(@PathVariable("username") username: String): ResponseEntity<Person>
|
||||
}
|
||||
|
|
|
@ -2,14 +2,16 @@ package dev.usbharu.hideout.controller
|
|||
|
||||
import dev.usbharu.hideout.domain.model.ap.Person
|
||||
import dev.usbharu.hideout.service.ap.APUserService
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
class UserAPControllerImpl(private val apUserService: APUserService) : UserAPController {
|
||||
override suspend fun userAp(username: String): ResponseEntity<Person> {
|
||||
override fun userAp(username: String): ResponseEntity<Person> = runBlocking {
|
||||
val person = apUserService.getPersonByName(username)
|
||||
return ResponseEntity(person, HttpStatus.OK)
|
||||
person.context += listOf("https://www.w3.org/ns/activitystreams")
|
||||
ResponseEntity(person, HttpStatus.OK)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.springframework.http.ResponseEntity
|
|||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import java.net.URL
|
||||
|
||||
@Controller
|
||||
class WebFingerController(
|
||||
|
@ -21,14 +20,14 @@ class WebFingerController(
|
|||
fun webfinger(@RequestParam("resource") resource: String): ResponseEntity<WebFinger> = runBlocking {
|
||||
val acct = AcctUtil.parse(resource.replace("acct:", ""))
|
||||
val user =
|
||||
webFingerApiService.findByNameAndDomain(acct.username, acct.domain ?: URL(applicationConfig.url).host)
|
||||
webFingerApiService.findByNameAndDomain(acct.username, acct.domain ?: applicationConfig.url.host)
|
||||
val webFinger = WebFinger(
|
||||
"acct:${user.name}@${user.domain}",
|
||||
listOf(
|
||||
WebFinger.Link(
|
||||
"self",
|
||||
"application/activity+json",
|
||||
applicationConfig.url + "/users/" + user.id
|
||||
user.url
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
package dev.usbharu.hideout.domain.model.job
|
||||
|
||||
import kjob.core.Job
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
sealed class HideoutJob(name: String = "") : Job(name)
|
||||
|
||||
@Component
|
||||
object ReceiveFollowJob : HideoutJob("ReceiveFollowJob") {
|
||||
val actor = string("actor")
|
||||
val follow = string("follow")
|
||||
val targetActor = string("targetActor")
|
||||
}
|
||||
|
||||
@Component
|
||||
object DeliverPostJob : HideoutJob("DeliverPostJob") {
|
||||
val post = string("post")
|
||||
val actor = string("actor")
|
||||
val inbox = string("inbox")
|
||||
}
|
||||
|
||||
@Component
|
||||
object DeliverReactionJob : HideoutJob("DeliverReactionJob") {
|
||||
val reaction = string("reaction")
|
||||
val postUrl = string("postUrl")
|
||||
|
@ -24,6 +28,7 @@ object DeliverReactionJob : HideoutJob("DeliverReactionJob") {
|
|||
val id = string("id")
|
||||
}
|
||||
|
||||
@Component
|
||||
object DeliverRemoveReactionJob : HideoutJob("DeliverRemoveReactionJob") {
|
||||
val id = string("id")
|
||||
val inbox = string("inbox")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package dev.usbharu.hideout.plugins
|
||||
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import dev.usbharu.hideout.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.domain.model.ap.JsonLd
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
|
@ -26,13 +27,18 @@ import java.text.SimpleDateFormat
|
|||
import java.util.*
|
||||
import javax.crypto.SecretKey
|
||||
|
||||
suspend fun HttpClient.postAp(urlString: String, username: String, jsonLd: JsonLd): HttpResponse {
|
||||
suspend fun HttpClient.postAp(
|
||||
urlString: String,
|
||||
username: String,
|
||||
jsonLd: JsonLd,
|
||||
objectMapper: ObjectMapper
|
||||
): HttpResponse {
|
||||
jsonLd.context += "https://www.w3.org/ns/activitystreams"
|
||||
return this.post(urlString) {
|
||||
header("Accept", ContentType.Application.Activity)
|
||||
header("Content-Type", ContentType.Application.Activity)
|
||||
header("Signature", "keyId=\"$username\",algorithm=\"rsa-sha256\",headers=\"(request-target) digest date\"")
|
||||
val text = Config.configData.objectMapper.writeValueAsString(jsonLd)
|
||||
val text = objectMapper.writeValueAsString(jsonLd)
|
||||
setBody(text)
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +163,11 @@ val httpSignaturePlugin = createClientPlugin("HttpSign", ::HttpSignaturePluginCo
|
|||
}
|
||||
}
|
||||
|
||||
class KtorKeyMap(private val userQueryService: UserQueryService, private val transaction: Transaction) : KeyMap {
|
||||
class KtorKeyMap(
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction,
|
||||
private val applicationConfig: ApplicationConfig
|
||||
) : KeyMap {
|
||||
override fun getPublicKey(keyId: String?): PublicKey = runBlocking {
|
||||
val username = (keyId ?: throw IllegalArgumentException("keyId is null")).substringBeforeLast("#pubkey")
|
||||
.substringAfterLast("/")
|
||||
|
@ -165,7 +175,7 @@ class KtorKeyMap(private val userQueryService: UserQueryService, private val tra
|
|||
transaction.transaction {
|
||||
userQueryService.findByNameAndDomain(
|
||||
username,
|
||||
Config.configData.domain
|
||||
applicationConfig.url.host
|
||||
).run {
|
||||
publicKey
|
||||
.replace("-----BEGIN PUBLIC KEY-----", "")
|
||||
|
@ -185,7 +195,7 @@ class KtorKeyMap(private val userQueryService: UserQueryService, private val tra
|
|||
transaction.transaction {
|
||||
userQueryService.findByNameAndDomain(
|
||||
username,
|
||||
Config.configData.domain
|
||||
applicationConfig.url.host
|
||||
).privateKey?.run {
|
||||
replace("-----BEGIN PRIVATE KEY-----", "")
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package dev.usbharu.hideout.service.ap
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.domain.model.ap.Create
|
||||
import dev.usbharu.hideout.domain.model.ap.Note
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||
|
@ -20,6 +21,7 @@ import io.ktor.client.*
|
|||
import io.ktor.client.statement.*
|
||||
import kjob.core.job.JobProps
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.Instant
|
||||
|
||||
|
@ -34,14 +36,17 @@ interface APNoteService {
|
|||
}
|
||||
|
||||
@Service
|
||||
class APNoteServiceImpl(
|
||||
class APNoteServiceImpl private constructor(
|
||||
private val httpClient: HttpClient,
|
||||
private val jobQueueParentService: JobQueueParentService,
|
||||
private val postRepository: PostRepository,
|
||||
private val apUserService: APUserService,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val followerQueryService: FollowerQueryService,
|
||||
private val postQueryService: PostQueryService
|
||||
private val postQueryService: PostQueryService,
|
||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper,
|
||||
private val applicationConfig: ApplicationConfig
|
||||
|
||||
) : APNoteService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
|
@ -49,7 +54,7 @@ class APNoteServiceImpl(
|
|||
override suspend fun createNote(post: Post) {
|
||||
val followers = followerQueryService.findFollowersById(post.userId)
|
||||
val userEntity = userQueryService.findById(post.userId)
|
||||
val note = Config.configData.objectMapper.writeValueAsString(post)
|
||||
val note = objectMapper.writeValueAsString(post)
|
||||
followers.forEach { followerEntity ->
|
||||
jobQueueParentService.schedule(DeliverPostJob) {
|
||||
props[DeliverPostJob.actor] = userEntity.url
|
||||
|
@ -61,7 +66,7 @@ class APNoteServiceImpl(
|
|||
|
||||
override suspend fun createNoteJob(props: JobProps<DeliverPostJob>) {
|
||||
val actor = props[DeliverPostJob.actor]
|
||||
val postEntity = Config.configData.objectMapper.readValue<Post>(props[DeliverPostJob.post])
|
||||
val postEntity = objectMapper.readValue<Post>(props[DeliverPostJob.post])
|
||||
val note = Note(
|
||||
name = "Note",
|
||||
id = postEntity.url,
|
||||
|
@ -79,8 +84,9 @@ class APNoteServiceImpl(
|
|||
name = "Create Note",
|
||||
`object` = note,
|
||||
actor = note.attributedTo,
|
||||
id = "${Config.configData.url}/create/note/${postEntity.id}"
|
||||
)
|
||||
id = "${applicationConfig.url}/create/note/${postEntity.id}"
|
||||
),
|
||||
objectMapper
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -95,7 +101,7 @@ class APNoteServiceImpl(
|
|||
url,
|
||||
targetActor?.let { "$targetActor#pubkey" }
|
||||
)
|
||||
val note = Config.configData.objectMapper.readValue<Note>(response.bodyAsText())
|
||||
val note = objectMapper.readValue<Note>(response.bodyAsText())
|
||||
return note(note, targetActor, url)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package dev.usbharu.hideout.service.ap
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.domain.model.ap.Like
|
||||
|
@ -14,6 +15,7 @@ import dev.usbharu.hideout.query.UserQueryService
|
|||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
||||
import io.ktor.client.*
|
||||
import kjob.core.job.JobProps
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.Instant
|
||||
|
||||
|
@ -31,8 +33,10 @@ class APReactionServiceImpl(
|
|||
private val httpClient: HttpClient,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val followerQueryService: FollowerQueryService,
|
||||
private val postQueryService: PostQueryService
|
||||
) : APReactionService {
|
||||
private val postQueryService: PostQueryService,
|
||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper,
|
||||
|
||||
) : APReactionService {
|
||||
override suspend fun reaction(like: Reaction) {
|
||||
val followers = followerQueryService.findFollowersById(like.userId)
|
||||
val user = userQueryService.findById(like.userId)
|
||||
|
@ -79,7 +83,7 @@ class APReactionServiceImpl(
|
|||
`object` = postUrl,
|
||||
id = "${Config.configData.url}/like/note/$id",
|
||||
content = content
|
||||
)
|
||||
), objectMapper
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -96,7 +100,7 @@ class APReactionServiceImpl(
|
|||
`object` = like,
|
||||
id = "${Config.configData.url}/undo/note/${like.id}",
|
||||
published = Instant.now()
|
||||
)
|
||||
), objectMapper
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package dev.usbharu.hideout.service.ap
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.domain.model.ActivityPubResponse
|
||||
import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
|
||||
import dev.usbharu.hideout.domain.model.ap.Accept
|
||||
|
@ -15,9 +15,10 @@ import dev.usbharu.hideout.service.user.UserService
|
|||
import io.ktor.client.*
|
||||
import io.ktor.http.*
|
||||
import kjob.core.job.JobProps
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
|
||||
interface APReceiveFollowService {
|
||||
suspend fun receiveFollow(follow: Follow): ActivityPubResponse
|
||||
suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>)
|
||||
|
@ -30,24 +31,26 @@ class APReceiveFollowServiceImpl(
|
|||
private val userService: UserService,
|
||||
private val httpClient: HttpClient,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction
|
||||
private val transaction: Transaction,
|
||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper
|
||||
) : APReceiveFollowService {
|
||||
override suspend fun receiveFollow(follow: Follow): ActivityPubResponse {
|
||||
// TODO: Verify HTTP Signature
|
||||
jobQueueParentService.schedule(ReceiveFollowJob) {
|
||||
props[ReceiveFollowJob.actor] = follow.actor
|
||||
props[ReceiveFollowJob.follow] = Config.configData.objectMapper.writeValueAsString(follow)
|
||||
props[ReceiveFollowJob.follow] = objectMapper.writeValueAsString(follow)
|
||||
props[ReceiveFollowJob.targetActor] = follow.`object`
|
||||
}
|
||||
return ActivityPubStringResponse(HttpStatusCode.OK, "{}", ContentType.Application.Json)
|
||||
}
|
||||
|
||||
override suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>) {
|
||||
// throw Exception()
|
||||
transaction.transaction {
|
||||
val actor = props[ReceiveFollowJob.actor]
|
||||
val targetActor = props[ReceiveFollowJob.targetActor]
|
||||
val person = apUserService.fetchPerson(actor, targetActor)
|
||||
val follow = Config.configData.objectMapper.readValue<Follow>(props[ReceiveFollowJob.follow])
|
||||
val follow = objectMapper.readValue<Follow>(props[ReceiveFollowJob.follow])
|
||||
httpClient.postAp(
|
||||
urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found"),
|
||||
username = "$targetActor#pubkey",
|
||||
|
@ -55,7 +58,7 @@ class APReceiveFollowServiceImpl(
|
|||
name = "Follow",
|
||||
`object` = follow,
|
||||
actor = targetActor
|
||||
)
|
||||
), objectMapper
|
||||
)
|
||||
|
||||
val targetEntity = userQueryService.findByUrl(targetActor)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package dev.usbharu.hideout.service.ap
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import dev.usbharu.hideout.domain.model.ap.Follow
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.SendFollowDto
|
||||
import dev.usbharu.hideout.plugins.postAp
|
||||
import io.ktor.client.*
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
|
@ -12,7 +14,10 @@ interface APSendFollowService {
|
|||
}
|
||||
|
||||
@Service
|
||||
class APSendFollowServiceImpl(private val httpClient: HttpClient) : APSendFollowService {
|
||||
class APSendFollowServiceImpl(
|
||||
private val httpClient: HttpClient,
|
||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper,
|
||||
) : APSendFollowService {
|
||||
override suspend fun sendFollow(sendFollowDto: SendFollowDto) {
|
||||
val follow = Follow(
|
||||
name = "Follow",
|
||||
|
@ -22,7 +27,8 @@ class APSendFollowServiceImpl(private val httpClient: HttpClient) : APSendFollow
|
|||
httpClient.postAp(
|
||||
urlString = sendFollowDto.followTargetUserId.inbox,
|
||||
username = sendFollowDto.userId.url,
|
||||
jsonLd = follow
|
||||
jsonLd = follow,
|
||||
objectMapper
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package dev.usbharu.hideout.service.ap
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.domain.model.ActivityPubResponse
|
||||
import dev.usbharu.hideout.domain.model.ap.Follow
|
||||
import dev.usbharu.hideout.domain.model.job.*
|
||||
|
@ -11,6 +11,7 @@ import kjob.core.dsl.JobContextWithProps
|
|||
import kjob.core.job.JobProps
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
|
@ -181,12 +182,13 @@ class APServiceImpl(
|
|||
private val apAcceptService: APAcceptService,
|
||||
private val apCreateService: APCreateService,
|
||||
private val apLikeService: APLikeService,
|
||||
private val apReactionService: APReactionService
|
||||
private val apReactionService: APReactionService,
|
||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper
|
||||
) : APService {
|
||||
|
||||
val logger: Logger = LoggerFactory.getLogger(this::class.java)
|
||||
override fun parseActivity(json: String): ActivityType {
|
||||
val readTree = Config.configData.objectMapper.readTree(json)
|
||||
val readTree = objectMapper.readTree(json)
|
||||
logger.trace("readTree: {}", readTree)
|
||||
if (readTree.isObject.not()) {
|
||||
throw JsonParseException("Json is not object.")
|
||||
|
@ -204,17 +206,17 @@ class APServiceImpl(
|
|||
override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse {
|
||||
logger.debug("proccess activity: {}", type)
|
||||
return when (type) {
|
||||
ActivityType.Accept -> apAcceptService.receiveAccept(Config.configData.objectMapper.readValue(json))
|
||||
ActivityType.Accept -> apAcceptService.receiveAccept(objectMapper.readValue(json))
|
||||
ActivityType.Follow -> apReceiveFollowService.receiveFollow(
|
||||
Config.configData.objectMapper.readValue(
|
||||
objectMapper.readValue(
|
||||
json,
|
||||
Follow::class.java
|
||||
)
|
||||
)
|
||||
|
||||
ActivityType.Create -> apCreateService.receiveCreate(Config.configData.objectMapper.readValue(json))
|
||||
ActivityType.Like -> apLikeService.receiveLike(Config.configData.objectMapper.readValue(json))
|
||||
ActivityType.Undo -> apUndoService.receiveUndo(Config.configData.objectMapper.readValue(json))
|
||||
ActivityType.Create -> apCreateService.receiveCreate(objectMapper.readValue(json))
|
||||
ActivityType.Like -> apLikeService.receiveLike(objectMapper.readValue(json))
|
||||
ActivityType.Undo -> apUndoService.receiveUndo(objectMapper.readValue(json))
|
||||
|
||||
else -> {
|
||||
throw IllegalArgumentException("$type is not supported.")
|
||||
|
@ -224,16 +226,25 @@ class APServiceImpl(
|
|||
|
||||
override suspend fun <T : HideoutJob> processActivity(job: JobContextWithProps<T>, hideoutJob: HideoutJob) {
|
||||
logger.debug("processActivity: ${hideoutJob.name}")
|
||||
when (hideoutJob) {
|
||||
ReceiveFollowJob -> apReceiveFollowService.receiveFollowJob(
|
||||
job.props as JobProps<ReceiveFollowJob>
|
||||
)
|
||||
|
||||
DeliverPostJob -> apNoteService.createNoteJob(job.props as JobProps<DeliverPostJob>)
|
||||
DeliverReactionJob -> apReactionService.reactionJob(job.props as JobProps<DeliverReactionJob>)
|
||||
DeliverRemoveReactionJob -> apReactionService.removeReactionJob(
|
||||
// println(apReceiveFollowService::class.java)
|
||||
// apReceiveFollowService.receiveFollowJob(job.props as JobProps<ReceiveFollowJob>)
|
||||
when {
|
||||
hideoutJob is ReceiveFollowJob -> {
|
||||
apReceiveFollowService.receiveFollowJob(
|
||||
job.props as JobProps<ReceiveFollowJob>
|
||||
)
|
||||
}
|
||||
|
||||
hideoutJob is DeliverPostJob -> apNoteService.createNoteJob(job.props as JobProps<DeliverPostJob>)
|
||||
hideoutJob is DeliverReactionJob -> apReactionService.reactionJob(job.props as JobProps<DeliverReactionJob>)
|
||||
hideoutJob is DeliverRemoveReactionJob -> apReactionService.removeReactionJob(
|
||||
job.props as JobProps<DeliverRemoveReactionJob>
|
||||
)
|
||||
|
||||
else -> {
|
||||
throw IllegalStateException("WTF")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package dev.usbharu.hideout.service.ap
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.domain.model.ap.Image
|
||||
import dev.usbharu.hideout.domain.model.ap.Key
|
||||
import dev.usbharu.hideout.domain.model.ap.Person
|
||||
|
@ -18,6 +19,7 @@ import io.ktor.client.*
|
|||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
|
@ -41,16 +43,18 @@ class APUserServiceImpl(
|
|||
private val userService: UserService,
|
||||
private val httpClient: HttpClient,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction
|
||||
private val transaction: Transaction,
|
||||
private val applicationConfig: ApplicationConfig,
|
||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper
|
||||
) :
|
||||
APUserService {
|
||||
|
||||
override suspend fun getPersonByName(name: String): Person {
|
||||
val userEntity = transaction.transaction {
|
||||
userQueryService.findByNameAndDomain(name, Config.configData.domain)
|
||||
userQueryService.findByNameAndDomain(name, applicationConfig.url.host)
|
||||
}
|
||||
// TODO: JOINで書き直し
|
||||
val userUrl = "${Config.configData.url}/users/$name"
|
||||
val userUrl = "${applicationConfig.url}/users/$name"
|
||||
return Person(
|
||||
type = emptyList(),
|
||||
name = userEntity.name,
|
||||
|
@ -73,7 +77,7 @@ class APUserServiceImpl(
|
|||
owner = userUrl,
|
||||
publicKeyPem = userEntity.publicKey
|
||||
),
|
||||
endpoints = mapOf("sharedInbox" to "${Config.configData.url}/inbox")
|
||||
endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox")
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -105,7 +109,7 @@ class APUserServiceImpl(
|
|||
owner = url,
|
||||
publicKeyPem = userEntity.publicKey
|
||||
),
|
||||
endpoints = mapOf("sharedInbox" to "${Config.configData.url}/inbox")
|
||||
endpoints = mapOf("sharedInbox" to "${applicationConfig.url}/inbox")
|
||||
) to userEntity
|
||||
} catch (ignore: FailedToGetResourcesException) {
|
||||
val httpResponse = if (targetActor != null) {
|
||||
|
@ -115,7 +119,7 @@ class APUserServiceImpl(
|
|||
accept(ContentType.Application.Activity)
|
||||
}
|
||||
}
|
||||
val person = Config.configData.objectMapper.readValue<Person>(httpResponse.bodyAsText())
|
||||
val person = objectMapper.readValue<Person>(httpResponse.bodyAsText())
|
||||
|
||||
person to userService.createRemoteUser(
|
||||
RemoteUserCreateDto(
|
||||
|
|
|
@ -3,7 +3,6 @@ package dev.usbharu.hideout.service.api.mastodon
|
|||
import dev.usbharu.hideout.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.*
|
||||
import org.springframework.stereotype.Service
|
||||
import java.net.URL
|
||||
|
||||
@Service
|
||||
interface InstanceApiService {
|
||||
|
@ -14,15 +13,14 @@ interface InstanceApiService {
|
|||
class InstanceApiServiceImpl(private val applicationConfig: ApplicationConfig) : InstanceApiService {
|
||||
override suspend fun v1Instance(): V1Instance {
|
||||
val url = applicationConfig.url
|
||||
val url1 = URL(url)
|
||||
return V1Instance(
|
||||
uri = url1.host,
|
||||
uri = url.host,
|
||||
title = "Hideout Server",
|
||||
shortDescription = "Hideout test server",
|
||||
description = "This server is operated for testing of Hideout. We are not responsible for any events that occur when associating with this server",
|
||||
email = "i@usbharu.dev",
|
||||
version = "0.0.1",
|
||||
urls = V1InstanceUrls("wss://${url1.host}"),
|
||||
urls = V1InstanceUrls("wss://${url.host}"),
|
||||
stats = V1InstanceStats(1, 0, 0),
|
||||
thumbnail = null,
|
||||
languages = listOf("ja-JP"),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package dev.usbharu.hideout.service.auth
|
||||
|
||||
import dev.usbharu.hideout.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.plugins.KtorKeyMap
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.core.Transaction
|
||||
|
@ -15,10 +16,13 @@ interface HttpSignatureVerifyService {
|
|||
@Service
|
||||
class HttpSignatureVerifyServiceImpl(
|
||||
private val userQueryService: UserQueryService,
|
||||
private val transaction: Transaction
|
||||
private val transaction: Transaction,
|
||||
private val applicationConfig: ApplicationConfig
|
||||
) : HttpSignatureVerifyService {
|
||||
override fun verify(headers: Headers): Boolean {
|
||||
val build = SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userQueryService, transaction)).build()
|
||||
val build =
|
||||
SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userQueryService, transaction, applicationConfig))
|
||||
.build()
|
||||
return true
|
||||
// build.verify(object : HttpMessage {
|
||||
// override fun headerValues(name: String?): MutableList<String> {
|
||||
|
|
|
@ -9,7 +9,6 @@ import org.springframework.security.core.userdetails.UserDetails
|
|||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||
import org.springframework.stereotype.Service
|
||||
import java.net.URL
|
||||
|
||||
@Service
|
||||
class UserDetailsServiceImpl(
|
||||
|
@ -23,7 +22,7 @@ class UserDetailsServiceImpl(
|
|||
throw UsernameNotFoundException("$username not found")
|
||||
}
|
||||
transaction.transaction {
|
||||
val findById = userQueryService.findByNameAndDomain(username, URL(applicationConfig.url).host)
|
||||
val findById = userQueryService.findByNameAndDomain(username, applicationConfig.url.host)
|
||||
UserDetailsImpl(
|
||||
findById.id,
|
||||
findById.name,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package dev.usbharu.hideout.service.job
|
||||
|
||||
import kjob.core.Job
|
||||
import dev.usbharu.hideout.domain.model.job.HideoutJob
|
||||
import kjob.core.dsl.KJobFunctions
|
||||
import org.springframework.stereotype.Service
|
||||
import kjob.core.dsl.JobContextWithProps as JCWP
|
||||
|
@ -8,5 +8,5 @@ import kjob.core.dsl.JobRegisterContext as JRC
|
|||
|
||||
@Service
|
||||
interface JobQueueWorkerService {
|
||||
fun init(defines: List<Pair<Job, JRC<Job, JCWP<Job>>.(Job) -> KJobFunctions<Job, JCWP<Job>>>>)
|
||||
fun init(defines: List<Pair<HideoutJob, JRC<HideoutJob, JCWP<HideoutJob>>.(HideoutJob) -> KJobFunctions<HideoutJob, JCWP<HideoutJob>>>>)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package dev.usbharu.hideout.service.job
|
||||
|
||||
import dev.usbharu.hideout.domain.model.job.HideoutJob
|
||||
import dev.usbharu.kjob.exposed.ExposedKJob
|
||||
import kjob.core.Job
|
||||
import kjob.core.dsl.JobContextWithProps
|
||||
import kjob.core.dsl.JobRegisterContext
|
||||
import kjob.core.dsl.KJobFunctions
|
||||
import kjob.core.kjob
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.springframework.stereotype.Service
|
||||
import kjob.core.dsl.JobContextWithProps as JCWP
|
||||
import kjob.core.dsl.JobRegisterContext as JRC
|
||||
|
||||
@Service
|
||||
class KJobJobQueueWorkerService(private val database: Database) : JobQueueWorkerService {
|
||||
|
@ -21,9 +21,7 @@ class KJobJobQueueWorkerService(private val database: Database) : JobQueueWorker
|
|||
}.start()
|
||||
}
|
||||
|
||||
override fun init(
|
||||
defines: List<Pair<Job, JRC<Job, JCWP<Job>>.(Job) -> KJobFunctions<Job, JCWP<Job>>>>
|
||||
) {
|
||||
override fun init(defines: List<Pair<HideoutJob, JobRegisterContext<HideoutJob, JobContextWithProps<HideoutJob>>.(HideoutJob) -> KJobFunctions<HideoutJob, JobContextWithProps<HideoutJob>>>>) {
|
||||
defines.forEach { job ->
|
||||
kjob.register(job.first, job.second)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package dev.usbharu.hideout.service.user
|
||||
|
||||
import dev.usbharu.hideout.config.Config
|
||||
import dev.usbharu.hideout.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.SendFollowDto
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
|
||||
|
@ -19,12 +19,13 @@ class UserServiceImpl(
|
|||
private val userAuthService: UserAuthService,
|
||||
private val apSendFollowService: APSendFollowService,
|
||||
private val userQueryService: UserQueryService,
|
||||
private val followerQueryService: FollowerQueryService
|
||||
private val followerQueryService: FollowerQueryService,
|
||||
private val applicationConfig: ApplicationConfig
|
||||
) :
|
||||
UserService {
|
||||
|
||||
override suspend fun usernameAlreadyUse(username: String): Boolean {
|
||||
val findByNameAndDomain = userQueryService.findByNameAndDomain(username, Config.configData.domain)
|
||||
val findByNameAndDomain = userQueryService.findByNameAndDomain(username, applicationConfig.url.host)
|
||||
return findByNameAndDomain != null
|
||||
}
|
||||
|
||||
|
@ -35,13 +36,13 @@ class UserServiceImpl(
|
|||
val userEntity = User.of(
|
||||
id = nextId,
|
||||
name = user.name,
|
||||
domain = Config.configData.domain,
|
||||
domain = applicationConfig.url.host,
|
||||
screenName = user.screenName,
|
||||
description = user.description,
|
||||
password = hashedPassword,
|
||||
inbox = "${Config.configData.url}/users/${user.name}/inbox",
|
||||
outbox = "${Config.configData.url}/users/${user.name}/outbox",
|
||||
url = "${Config.configData.url}/users/${user.name}",
|
||||
inbox = "${applicationConfig.url}/users/${user.name}/inbox",
|
||||
outbox = "${applicationConfig.url}/users/${user.name}/outbox",
|
||||
url = "${applicationConfig.url}/users/${user.name}",
|
||||
publicKey = keyPair.public.toPem(),
|
||||
privateKey = keyPair.private.toPem(),
|
||||
createdAt = Instant.now()
|
||||
|
@ -70,7 +71,7 @@ class UserServiceImpl(
|
|||
override suspend fun followRequest(id: Long, followerId: Long): Boolean {
|
||||
val user = userRepository.findById(id) ?: throw UserNotFoundException("$id was not found.")
|
||||
val follower = userRepository.findById(followerId) ?: throw UserNotFoundException("$followerId was not found.")
|
||||
return if (user.domain == Config.configData.domain) {
|
||||
return if (user.domain == applicationConfig.url.host) {
|
||||
follow(id, followerId)
|
||||
true
|
||||
} else {
|
||||
|
|
|
@ -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="DEBUG">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<logger name="org.eclipse.jetty" level="INFO"/>
|
||||
|
@ -12,5 +12,5 @@
|
|||
<logger name="kjob.core.internal.scheduler.JobServiceImpl" level="INFO"/>
|
||||
<logger name="Exposed" level="INFO"/>
|
||||
<logger name="io.ktor.server.plugins.contentnegotiation" level="INFO"/>
|
||||
<logger name="org.springframework.security" level="trace"/>
|
||||
<logger name="org.springframework.security" level="DEBUG"/>
|
||||
</configuration>
|
||||
|
|
Loading…
Reference in New Issue