mirror of https://github.com/usbharu/Hideout.git
commit
2fc5962ddd
|
@ -0,0 +1,58 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:16
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_USER: "postgres"
|
||||
POSTGRES_PASSWORD: "password"
|
||||
POSTGRES_DB: "hideout"
|
||||
|
||||
mongodb:
|
||||
image: mongo:7.0.14
|
||||
ports:
|
||||
- "27017:27017"
|
||||
|
||||
elasticsearch:
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.24
|
||||
container_name: elasticsearch
|
||||
environment:
|
||||
- discovery.type=single-node
|
||||
- cluster.name=docker-cluster
|
||||
- bootstrap.memory_lock=true
|
||||
- xpack.security.enabled=true
|
||||
- xpack.monitoring.collection.enabled=true
|
||||
- ELASTIC_PASSWORD=Passw0rd
|
||||
- "ES_JAVA_OPTS=-Xms512M -Xmx512M"
|
||||
ports:
|
||||
- "9200:9200"
|
||||
volumes:
|
||||
- elasticsearch-data:/usr/share/elasticsearch/data
|
||||
kibana:
|
||||
image: docker.elastic.co/kibana/kibana:7.17.24
|
||||
container_name: kibana
|
||||
ports:
|
||||
- "5601:5601"
|
||||
environment:
|
||||
- ELASTICSEARCH_URL=http://elasticsearch:9200
|
||||
- ELASTICSEARCH_USERNAME=elastic
|
||||
- ELASTICSEARCH_PASSWORD=Passw0rd
|
||||
depends_on:
|
||||
- elasticsearch
|
||||
|
||||
fluentd:
|
||||
build: ./fluentd
|
||||
volumes:
|
||||
- ./fluentd/conf:/fluentd/etc
|
||||
- ../logs:/in
|
||||
depends_on:
|
||||
- "elasticsearch"
|
||||
ports:
|
||||
- "24224:24224"
|
||||
- "24224:24224/udp"
|
||||
|
||||
volumes:
|
||||
elasticsearch-data:
|
||||
driver: local
|
|
@ -0,0 +1,4 @@
|
|||
FROM fluent/fluentd:v1.16-debian-2
|
||||
USER root
|
||||
RUN fluent-gem install fluent-plugin-elasticsearch --no-document
|
||||
USER fluent
|
|
@ -0,0 +1,22 @@
|
|||
<source>
|
||||
@type tail
|
||||
path /in/logFile.log.json
|
||||
pos_file /in/logFile.pos
|
||||
tag hideout
|
||||
<parse>
|
||||
@type json
|
||||
</parse>
|
||||
</source>
|
||||
|
||||
<match hideout>
|
||||
@type elasticsearch
|
||||
host elasticsearch
|
||||
include_tag_key true
|
||||
port 9200
|
||||
include_timestamp true
|
||||
user elastic
|
||||
password Passw0rd
|
||||
logstash_format true
|
||||
logstash_prefix hideout
|
||||
flush_interval 10s
|
||||
</match>
|
|
@ -1,16 +0,0 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:16
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_USER: "postgres"
|
||||
POSTGRES_PASSWORD: "password"
|
||||
POSTGRES_DB: "hideout"
|
||||
|
||||
mongodb:
|
||||
image: mongo:7.0.14
|
||||
ports:
|
||||
- "27017:27017"
|
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -5,6 +5,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
|||
|
||||
interface DomainEventSubscriber {
|
||||
fun <T : DomainEventBody> subscribe(eventName: String, domainEventConsumer: DomainEventConsumer<T>)
|
||||
fun getSubscribers(): Map<String, List<DomainEventConsumer<*>>>
|
||||
}
|
||||
|
||||
typealias DomainEventConsumer<T> = suspend (DomainEvent<T>) -> Unit
|
||||
|
|
|
@ -3,19 +3,22 @@ package dev.usbharu.hideout.core.application.domainevent.subscribers
|
|||
import dev.usbharu.hideout.core.domain.event.userdetail.UserDetailEvent
|
||||
import dev.usbharu.hideout.core.domain.event.userdetail.UserDetailEventBody
|
||||
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class RegisterLocalUserSetHomeTimelineSubscriber(
|
||||
private val domainEventSubscriber: DomainEventSubscriber,
|
||||
private val userRegisterHomeTimelineApplicationService: UserRegisterHomeTimelineApplicationService
|
||||
) : Subscriber {
|
||||
) : Subscriber, DomainEventConsumer<UserDetailEventBody> {
|
||||
override fun init() {
|
||||
domainEventSubscriber.subscribe<UserDetailEventBody>(UserDetailEvent.CREATE.eventName) {
|
||||
userRegisterHomeTimelineApplicationService.execute(
|
||||
RegisterHomeTimeline(it.body.getUserDetail().id),
|
||||
Anonymous
|
||||
)
|
||||
}
|
||||
domainEventSubscriber.subscribe<UserDetailEventBody>(UserDetailEvent.CREATE.eventName, this)
|
||||
}
|
||||
|
||||
override suspend fun invoke(p1: DomainEvent<UserDetailEventBody>) {
|
||||
userRegisterHomeTimelineApplicationService.execute(
|
||||
RegisterHomeTimeline(p1.body.getUserDetail().id),
|
||||
Anonymous
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,17 +5,20 @@ import dev.usbharu.hideout.core.application.timeline.SetTimleineStore
|
|||
import dev.usbharu.hideout.core.domain.event.timeline.TimelineEvent
|
||||
import dev.usbharu.hideout.core.domain.event.timeline.TimelineEventBody
|
||||
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class RegisterTimelineSetTimelineStoreSubscriber(
|
||||
private val domainEventSubscriber: DomainEventSubscriber,
|
||||
private val setTimelineToTimelineStoreApplicationService: SetTimelineToTimelineStoreApplicationService
|
||||
) : Subscriber {
|
||||
) : Subscriber, DomainEventConsumer<TimelineEventBody> {
|
||||
|
||||
override fun init() {
|
||||
domainEventSubscriber.subscribe<TimelineEventBody>(TimelineEvent.CREATE.eventName) {
|
||||
setTimelineToTimelineStoreApplicationService.execute(SetTimleineStore(it.body.getTimelineId()), Anonymous)
|
||||
}
|
||||
domainEventSubscriber.subscribe<TimelineEventBody>(TimelineEvent.CREATE.eventName, this)
|
||||
}
|
||||
|
||||
override suspend fun invoke(p1: DomainEvent<TimelineEventBody>) {
|
||||
setTimelineToTimelineStoreApplicationService.execute(SetTimleineStore(p1.body.getTimelineId()), Anonymous)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,46 @@
|
|||
package dev.usbharu.hideout.core.application.domainevent.subscribers
|
||||
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.boot.ApplicationArguments
|
||||
import org.springframework.boot.ApplicationRunner
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class SubscriberRunner(private val subscribers: List<Subscriber>) : ApplicationRunner {
|
||||
class SubscriberRunner(
|
||||
private val subscribers: List<Subscriber>,
|
||||
private val domainEventSubscriber: DomainEventSubscriber
|
||||
) : ApplicationRunner {
|
||||
override fun run(args: ApplicationArguments?) {
|
||||
subscribers.forEach { it.init() }
|
||||
|
||||
if (logger.isDebugEnabled) {
|
||||
val stringListMap = domainEventSubscriber.getSubscribers()
|
||||
|
||||
val header = """
|
||||
|====== Domain Event Subscribers Report =====
|
||||
|
|
||||
|
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val value = stringListMap.map {
|
||||
it.key + "\n\t" + it.value.joinToString("\n", "[", "]") { suspendFunction1 ->
|
||||
suspendFunction1::class.qualifiedName.orEmpty()
|
||||
}
|
||||
}.joinToString("\n\n\n")
|
||||
|
||||
val footer = """
|
||||
|
|
||||
|
|
||||
|
|
||||
|===== Domain Event Subscribers Report =====
|
||||
""".trimMargin()
|
||||
|
||||
logger.debug("{}{}{}", header, value, footer)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(SubscriberRunner::class.java)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,16 +5,19 @@ import dev.usbharu.hideout.core.application.timeline.TimelineAddPostApplicationS
|
|||
import dev.usbharu.hideout.core.domain.event.post.PostEvent
|
||||
import dev.usbharu.hideout.core.domain.event.post.PostEventBody
|
||||
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class TimelinePostCreateSubscriber(
|
||||
private val timelineAddPostApplicationService: TimelineAddPostApplicationService,
|
||||
private val domainEventSubscriber: DomainEventSubscriber,
|
||||
) : Subscriber {
|
||||
) : Subscriber, DomainEventConsumer<PostEventBody> {
|
||||
override fun init() {
|
||||
domainEventSubscriber.subscribe<PostEventBody>(PostEvent.CREATE.eventName) {
|
||||
timelineAddPostApplicationService.execute(AddPost(it.body.getPostId()), Anonymous)
|
||||
}
|
||||
domainEventSubscriber.subscribe<PostEventBody>(PostEvent.CREATE.eventName, this)
|
||||
}
|
||||
|
||||
override suspend fun invoke(p1: DomainEvent<PostEventBody>) {
|
||||
timelineAddPostApplicationService.execute(AddPost(p1.body.getPostId()), Anonymous)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEvent
|
|||
import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventBody
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
|
@ -15,28 +16,30 @@ class TimelineRelationshipFollowSubscriber(
|
|||
private val userAddTimelineRelationshipApplicationService: UserAddTimelineRelationshipApplicationService,
|
||||
private val userDetailRepository: UserDetailRepository,
|
||||
private val domainEventSubscriber: DomainEventSubscriber
|
||||
) : Subscriber {
|
||||
) : Subscriber, DomainEventConsumer<RelationshipEventBody> {
|
||||
|
||||
override fun init() {
|
||||
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.ACCEPT_FOLLOW.eventName) {
|
||||
val relationship = it.body.getRelationship()
|
||||
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id)
|
||||
?: throw InternalServerException("Userdetail ${relationship.actorId} not found by actorid.")
|
||||
if (userDetail.homeTimelineId == null) {
|
||||
logger.warn("Home timeline for ${relationship.actorId} is not found")
|
||||
return@subscribe
|
||||
}
|
||||
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.ACCEPT_FOLLOW.eventName, this)
|
||||
}
|
||||
|
||||
@Suppress("UnsafeCallOnNullableType")
|
||||
userAddTimelineRelationshipApplicationService.execute(
|
||||
AddTimelineRelationship(
|
||||
userDetail.homeTimelineId!!,
|
||||
relationship.targetActorId,
|
||||
Visible.FOLLOWERS
|
||||
),
|
||||
it.body.principal
|
||||
)
|
||||
override suspend fun invoke(p1: DomainEvent<RelationshipEventBody>) {
|
||||
val relationship = p1.body.getRelationship()
|
||||
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id)
|
||||
?: throw InternalServerException("Userdetail ${relationship.actorId} not found by actorid.")
|
||||
if (userDetail.homeTimelineId == null) {
|
||||
logger.warn("Home timeline for ${relationship.actorId} is not found")
|
||||
return
|
||||
}
|
||||
|
||||
@Suppress("UnsafeCallOnNullableType")
|
||||
userAddTimelineRelationshipApplicationService.execute(
|
||||
AddTimelineRelationship(
|
||||
userDetail.homeTimelineId!!,
|
||||
relationship.targetActorId,
|
||||
Visible.FOLLOWERS
|
||||
),
|
||||
p1.body.principal
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -6,6 +6,7 @@ import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEvent
|
|||
import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventBody
|
||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
|
@ -15,29 +16,31 @@ class TimelineRelationshipUnfollowSubscriber(
|
|||
private val userRemoveTimelineRelationshipApplicationService: UserRemoveTimelineRelationshipApplicationService,
|
||||
private val userDetailRepository: UserDetailRepository,
|
||||
private val timelineRelationshipRepository: TimelineRelationshipRepository,
|
||||
) : Subscriber {
|
||||
) : Subscriber, DomainEventConsumer<RelationshipEventBody> {
|
||||
override fun init() {
|
||||
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.UNFOLLOW.eventName) {
|
||||
val relationship = it.body.getRelationship()
|
||||
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id) ?: throw IllegalStateException(
|
||||
"UserDetail ${relationship.actorId} not found by actorId."
|
||||
)
|
||||
if (userDetail.homeTimelineId == null) {
|
||||
logger.warn("HomeTimeline for ${userDetail.id} not found.")
|
||||
return@subscribe
|
||||
}
|
||||
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.UNFOLLOW.eventName, this)
|
||||
}
|
||||
|
||||
val timelineRelationship = timelineRelationshipRepository.findByTimelineIdAndActorId(
|
||||
userDetail.homeTimelineId!!,
|
||||
relationship.targetActorId
|
||||
)
|
||||
?: throw IllegalStateException("TimelineRelationship ${userDetail.homeTimelineId} to ${relationship.targetActorId} not found by timelineId and ActorId")
|
||||
|
||||
userRemoveTimelineRelationshipApplicationService.execute(
|
||||
RemoveTimelineRelationship(timelineRelationship.id),
|
||||
it.body.principal
|
||||
)
|
||||
override suspend fun invoke(p1: DomainEvent<RelationshipEventBody>) {
|
||||
val relationship = p1.body.getRelationship()
|
||||
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id) ?: throw IllegalStateException(
|
||||
"UserDetail ${relationship.actorId} not found by actorId."
|
||||
)
|
||||
if (userDetail.homeTimelineId == null) {
|
||||
logger.warn("HomeTimeline for ${userDetail.id} not found.")
|
||||
return
|
||||
}
|
||||
|
||||
val timelineRelationship = timelineRelationshipRepository.findByTimelineIdAndActorId(
|
||||
userDetail.homeTimelineId!!,
|
||||
relationship.targetActorId
|
||||
)
|
||||
?: throw IllegalStateException("TimelineRelationship ${userDetail.homeTimelineId} to ${relationship.targetActorId} not found by timelineId and ActorId")
|
||||
|
||||
userRemoveTimelineRelationshipApplicationService.execute(
|
||||
RemoveTimelineRelationship(timelineRelationship.id),
|
||||
p1.body.principal
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -19,6 +19,7 @@ package dev.usbharu.hideout.core.application.shared
|
|||
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.MDC
|
||||
|
||||
abstract class AbstractApplicationService<T : Any, R>(
|
||||
protected val transaction: Transaction,
|
||||
|
@ -26,6 +27,7 @@ abstract class AbstractApplicationService<T : Any, R>(
|
|||
) : ApplicationService<T, R> {
|
||||
override suspend fun execute(command: T, principal: Principal): R {
|
||||
return try {
|
||||
MDC.put("applicationService", this::class.simpleName)
|
||||
logger.debug("START {}", command::class.simpleName)
|
||||
val response = transaction.transaction<R> {
|
||||
internalExecute(command, principal)
|
||||
|
@ -39,6 +41,8 @@ abstract class AbstractApplicationService<T : Any, R>(
|
|||
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
|
||||
logger.warn("Command execution error", e)
|
||||
throw e
|
||||
} finally {
|
||||
MDC.remove("applicationService")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,11 @@
|
|||
|
||||
package dev.usbharu.hideout.core.config
|
||||
|
||||
import ch.qos.logback.classic.helpers.MDCInsertingServletFilter
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.ApplicationRequestLogInterceptor
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.SPAInterceptor
|
||||
import dev.usbharu.hideout.generate.JsonOrFormModelMethodProcessor
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.http.converter.HttpMessageConverter
|
||||
|
@ -30,7 +33,8 @@ import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttribu
|
|||
@Configuration
|
||||
class MvcConfigurer(
|
||||
private val jsonOrFormModelMethodProcessor: JsonOrFormModelMethodProcessor,
|
||||
private val spaInterceptor: SPAInterceptor
|
||||
private val spaInterceptor: SPAInterceptor,
|
||||
private val applicationRequestLogInterceptor: ApplicationRequestLogInterceptor
|
||||
) : WebMvcConfigurer {
|
||||
override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
|
||||
resolvers.add(jsonOrFormModelMethodProcessor)
|
||||
|
@ -38,6 +42,16 @@ class MvcConfigurer(
|
|||
|
||||
override fun addInterceptors(registry: InterceptorRegistry) {
|
||||
registry.addInterceptor(spaInterceptor)
|
||||
registry.addInterceptor(applicationRequestLogInterceptor)
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun mdcFilter(): FilterRegistrationBean<MDCInsertingServletFilter> {
|
||||
val bean = FilterRegistrationBean<MDCInsertingServletFilter>()
|
||||
bean.filter = MDCInsertingServletFilter()
|
||||
bean.addUrlPatterns("/*")
|
||||
bean.order = Int.MIN_VALUE
|
||||
return bean
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.springframework
|
||||
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.HideoutUserDetails
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.oauth2.jwt.Jwt
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.web.servlet.AsyncHandlerInterceptor
|
||||
import java.util.*
|
||||
|
||||
@Component
|
||||
class ApplicationRequestLogInterceptor : AsyncHandlerInterceptor {
|
||||
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
|
||||
MDC.put(requestId, UUID.randomUUID().toString())
|
||||
MDC.put(Companion.handler, handler.toString())
|
||||
val userDetailId = when (val principal = SecurityContextHolder.getContext().authentication?.principal) {
|
||||
is HideoutUserDetails -> {
|
||||
principal.userDetailsId
|
||||
}
|
||||
|
||||
is Jwt -> {
|
||||
principal.getClaim<String>("uid")?.toLongOrNull()
|
||||
}
|
||||
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (userDetailId != null) {
|
||||
MDC.put(userId, userDetailId.toString())
|
||||
}
|
||||
|
||||
logger.info("START")
|
||||
return true
|
||||
}
|
||||
|
||||
override fun afterCompletion(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
handler: Any,
|
||||
ex: Exception?
|
||||
) {
|
||||
removeMdc()
|
||||
}
|
||||
|
||||
override fun afterConcurrentHandlingStarted(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
handler: Any
|
||||
) {
|
||||
removeMdc()
|
||||
}
|
||||
|
||||
private fun removeMdc() {
|
||||
MDC.remove(requestId)
|
||||
MDC.remove(userId)
|
||||
MDC.remove(handler)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val requestId: String = "requestId"
|
||||
const val userId: String = "userId"
|
||||
const val handler: String = "handler"
|
||||
private val logger = LoggerFactory.getLogger(ApplicationRequestLogInterceptor::class.java)
|
||||
}
|
||||
}
|
|
@ -18,7 +18,9 @@ package dev.usbharu.hideout.core.infrastructure.springframework.domainevent
|
|||
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.ApplicationRequestLogInterceptor
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.context.ApplicationEventPublisher
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
|
@ -27,10 +29,19 @@ class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher:
|
|||
DomainEventPublisher {
|
||||
override suspend fun publishEvent(domainEvent: DomainEvent<*>) {
|
||||
logger.trace("Publish ${domainEvent.id} ${domainEvent.name}")
|
||||
applicationEventPublisher.publishEvent(domainEvent)
|
||||
|
||||
val requestId: String? = MDC.get(ApplicationRequestLogInterceptor.requestId)
|
||||
val springDomainEvent = SpringDomainEvent(
|
||||
requestId,
|
||||
domainEvent
|
||||
)
|
||||
|
||||
applicationEventPublisher.publishEvent(springDomainEvent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(SpringFrameworkDomainEventPublisher::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
data class SpringDomainEvent(val requestId: String?, val domainEvent: DomainEvent<*>)
|
||||
|
|
|
@ -2,9 +2,14 @@ package dev.usbharu.hideout.core.infrastructure.springframework.domainevent
|
|||
|
||||
import dev.usbharu.hideout.core.application.domainevent.subscribers.DomainEventConsumer
|
||||
import dev.usbharu.hideout.core.application.domainevent.subscribers.DomainEventSubscriber
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.ApplicationRequestLogInterceptor.Companion.requestId
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.slf4j.MDCContext
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.context.event.EventListener
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
|
@ -17,15 +22,30 @@ class SpringFrameworkDomainEventSubscriber : DomainEventSubscriber {
|
|||
map.getOrPut(eventName) { mutableListOf() }.add(domainEventConsumer as DomainEventConsumer<*>)
|
||||
}
|
||||
|
||||
override fun getSubscribers(): Map<String, List<DomainEventConsumer<*>>> = map
|
||||
|
||||
@EventListener
|
||||
suspend fun onDomainEventPublished(domainEvent: DomainEvent<*>) {
|
||||
logger.trace("Domain Event Published: $domainEvent")
|
||||
map[domainEvent.name]?.forEach {
|
||||
try {
|
||||
it.invoke(domainEvent)
|
||||
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
|
||||
logger.error("", e)
|
||||
}
|
||||
suspend fun onDomainEventPublished(domainEvent: SpringDomainEvent) {
|
||||
logger.debug(
|
||||
"Domain Event Published: {} id: {} requestId: {}",
|
||||
domainEvent.domainEvent.name,
|
||||
domainEvent.domainEvent.id,
|
||||
domainEvent.requestId
|
||||
)
|
||||
coroutineScope {
|
||||
map[domainEvent.domainEvent.name]?.map {
|
||||
async(MDCContext()) {
|
||||
try {
|
||||
MDC.put(requestId, domainEvent.requestId)
|
||||
it.invoke(domainEvent.domainEvent)
|
||||
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
|
||||
logger.warn("", e)
|
||||
null
|
||||
} finally {
|
||||
MDC.remove(requestId)
|
||||
}
|
||||
}
|
||||
}?.awaitAll()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import dev.usbharu.hideout.core.config.ApplicationConfig
|
|||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.slf4j.MDCContext
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||
|
@ -33,7 +34,7 @@ class UserDetailsServiceImpl(
|
|||
private val applicationConfig: ApplicationConfig,
|
||||
private val transaction: Transaction,
|
||||
) : UserDetailsService {
|
||||
override fun loadUserByUsername(username: String?): UserDetails = runBlocking {
|
||||
override fun loadUserByUsername(username: String?): UserDetails = runBlocking(MDCContext()) {
|
||||
if (username == null) {
|
||||
throw UsernameNotFoundException("Username not found")
|
||||
}
|
||||
|
|
|
@ -12,4 +12,8 @@
|
|||
<appender-ref ref="ECS_JSON_FILE"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
<logger name="javax.management" level="INFO"/>
|
||||
<logger name="sun.rmi.loader" level="INFO"/>
|
||||
<logger name="sun.rmi" level="INFO"/>
|
||||
<logger name="java.io.serialization" level="INFO"/>
|
||||
</configuration>
|
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -8,6 +8,4 @@ class MastodonReadTimeline(
|
|||
val localOnly: Boolean,
|
||||
val remoteOnly: Boolean,
|
||||
val page: Page
|
||||
) {
|
||||
|
||||
}
|
||||
)
|
||||
|
|
|
@ -38,72 +38,76 @@ class MastodonReadTimelineApplicationService(
|
|||
|
||||
val readTimeline = timelineStore.readTimeline(timeline, readTimelineOption, command.page, principal)
|
||||
|
||||
return PaginationList(readTimeline.map {
|
||||
Status(
|
||||
it.postId.id.toString(),
|
||||
it.post.url.toString(),
|
||||
it.post.createdAt.toString(),
|
||||
account = Account(
|
||||
id = it.postActor.id.id.toString(),
|
||||
username = it.postActor.name.name,
|
||||
acct = Acct(it.postActor.name.name, it.postActor.domain.domain).toString(),
|
||||
url = it.postActor.url.toString(),
|
||||
displayName = it.postActor.screenName.screenName,
|
||||
note = it.postActor.description.description,
|
||||
avatar = it.postActorIconMedia?.url.toString(),
|
||||
avatarStatic = it.postActorIconMedia?.thumbnailUrl.toString(),
|
||||
header = "",
|
||||
headerStatic = "",
|
||||
locked = false,
|
||||
fields = emptyList(),
|
||||
return PaginationList(
|
||||
readTimeline.map {
|
||||
Status(
|
||||
it.postId.id.toString(),
|
||||
it.post.url.toString(),
|
||||
it.post.createdAt.toString(),
|
||||
account = Account(
|
||||
id = it.postActor.id.id.toString(),
|
||||
username = it.postActor.name.name,
|
||||
acct = Acct(it.postActor.name.name, it.postActor.domain.domain).toString(),
|
||||
url = it.postActor.url.toString(),
|
||||
displayName = it.postActor.screenName.screenName,
|
||||
note = it.postActor.description.description,
|
||||
avatar = it.postActorIconMedia?.url.toString(),
|
||||
avatarStatic = it.postActorIconMedia?.thumbnailUrl.toString(),
|
||||
header = "",
|
||||
headerStatic = "",
|
||||
locked = false,
|
||||
fields = emptyList(),
|
||||
emojis = emptyList(),
|
||||
bot = false,
|
||||
group = false,
|
||||
discoverable = true,
|
||||
createdAt = it.postActor.createdAt.toString(),
|
||||
statusesCount = it.postActor.postsCount.postsCount,
|
||||
noindex = true,
|
||||
moved = it.postActor.moveTo != null,
|
||||
suspended = it.postActor.suspend,
|
||||
limited = false,
|
||||
lastStatusAt = it.postActor.lastPostAt?.toString(),
|
||||
followersCount = it.postActor.followersCount?.relationshipCount,
|
||||
followingCount = it.postActor.followingCount?.relationshipCount,
|
||||
),
|
||||
content = it.post.content.content,
|
||||
visibility = when (it.post.visibility) {
|
||||
PUBLIC -> Status.Visibility.public
|
||||
UNLISTED -> Status.Visibility.unlisted
|
||||
FOLLOWERS -> Status.Visibility.private
|
||||
DIRECT -> Status.Visibility.direct
|
||||
},
|
||||
sensitive = it.post.sensitive,
|
||||
spoilerText = it.post.overview?.overview.orEmpty(),
|
||||
mediaAttachments = it.postMedias.map { MediaAttachment(it.id.id.toString()) },
|
||||
mentions = emptyList(),
|
||||
tags = emptyList(),
|
||||
emojis = emptyList(),
|
||||
bot = false,
|
||||
group = false,
|
||||
discoverable = true,
|
||||
createdAt = it.postActor.createdAt.toString(),
|
||||
statusesCount = it.postActor.postsCount.postsCount,
|
||||
noindex = true,
|
||||
moved = it.postActor.moveTo != null,
|
||||
suspended = it.postActor.suspend,
|
||||
limited = false,
|
||||
lastStatusAt = it.postActor.lastPostAt?.toString(),
|
||||
followersCount = it.postActor.followersCount?.relationshipCount,
|
||||
followingCount = it.postActor.followingCount?.relationshipCount,
|
||||
),
|
||||
content = it.post.content.content,
|
||||
visibility = when (it.post.visibility) {
|
||||
PUBLIC -> Status.Visibility.public
|
||||
UNLISTED -> Status.Visibility.unlisted
|
||||
FOLLOWERS -> Status.Visibility.private
|
||||
DIRECT -> Status.Visibility.direct
|
||||
},
|
||||
sensitive = it.post.sensitive,
|
||||
spoilerText = it.post.overview?.overview.orEmpty(),
|
||||
mediaAttachments = it.postMedias.map { MediaAttachment(it.id.id.toString()) },
|
||||
mentions = emptyList(),
|
||||
tags = emptyList(),
|
||||
emojis = emptyList(),
|
||||
reblogsCount = 0,
|
||||
favouritesCount = it.reactionsList.sumOf { it.count },
|
||||
repliesCount = 0,
|
||||
url = it.post.url.toString(),
|
||||
text = it.post.content.text,
|
||||
application = null,
|
||||
inReplyToId = it.replyPost?.id?.toString(),
|
||||
inReplyToAccountId = it.replyPostActor?.id?.toString(),
|
||||
reblog = null,
|
||||
poll = null,
|
||||
card = null,
|
||||
language = null,
|
||||
editedAt = null,
|
||||
favourited = it.favourited,
|
||||
reblogged = false,
|
||||
muted = false,
|
||||
bookmarked = false,
|
||||
pinned = false,
|
||||
filtered = emptyList(),
|
||||
)
|
||||
}, readTimeline.next?.id, readTimeline.prev?.id)
|
||||
reblogsCount = 0,
|
||||
favouritesCount = it.reactionsList.sumOf { it.count },
|
||||
repliesCount = 0,
|
||||
url = it.post.url.toString(),
|
||||
text = it.post.content.text,
|
||||
application = null,
|
||||
inReplyToId = it.replyPost?.id?.toString(),
|
||||
inReplyToAccountId = it.replyPostActor?.id?.toString(),
|
||||
reblog = null,
|
||||
poll = null,
|
||||
card = null,
|
||||
language = null,
|
||||
editedAt = null,
|
||||
favourited = it.favourited,
|
||||
reblogged = false,
|
||||
muted = false,
|
||||
bookmarked = false,
|
||||
pinned = false,
|
||||
filtered = emptyList(),
|
||||
)
|
||||
},
|
||||
readTimeline.next?.id,
|
||||
readTimeline.prev?.id
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -49,6 +49,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.slf4j.MDCContext
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.stereotype.Controller
|
||||
|
||||
|
@ -164,7 +165,7 @@ class SpringAccountApi(
|
|||
id: List<String>?,
|
||||
withSuspended: Boolean
|
||||
): ResponseEntity<Flow<Relationship>> {
|
||||
val principal = runBlocking { principalContextHolder.getPrincipal() }
|
||||
val principal = runBlocking(MDCContext()) { principalContextHolder.getPrincipal() }
|
||||
return ResponseEntity.ok(id.orEmpty().asFlow().mapNotNull { fetchRelationship(it, principal).body })
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.slf4j.MDCContext
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.stereotype.Controller
|
||||
|
||||
|
@ -44,7 +45,7 @@ class SpringTimelineApi(
|
|||
sinceId: String?,
|
||||
minId: String?,
|
||||
limit: Int?
|
||||
): ResponseEntity<Flow<Status>> = runBlocking {
|
||||
): ResponseEntity<Flow<Status>> = runBlocking(MDCContext()) {
|
||||
val principal = principalContextHolder.getPrincipal()
|
||||
val userDetail = transaction.transaction {
|
||||
userDetailRepository.findByActorId(principal.actorId.id)
|
||||
|
@ -67,7 +68,8 @@ class SpringTimelineApi(
|
|||
minId?.toLongOrNull(),
|
||||
limit
|
||||
)
|
||||
), principal
|
||||
),
|
||||
principal
|
||||
).asFlow()
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue