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
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
|
@ -5,6 +5,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
|
||||||
|
|
||||||
interface DomainEventSubscriber {
|
interface DomainEventSubscriber {
|
||||||
fun <T : DomainEventBody> subscribe(eventName: String, domainEventConsumer: DomainEventConsumer<T>)
|
fun <T : DomainEventBody> subscribe(eventName: String, domainEventConsumer: DomainEventConsumer<T>)
|
||||||
|
fun getSubscribers(): Map<String, List<DomainEventConsumer<*>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias DomainEventConsumer<T> = suspend (DomainEvent<T>) -> Unit
|
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.UserDetailEvent
|
||||||
import dev.usbharu.hideout.core.domain.event.userdetail.UserDetailEventBody
|
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.model.support.principal.Anonymous
|
||||||
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class RegisterLocalUserSetHomeTimelineSubscriber(
|
class RegisterLocalUserSetHomeTimelineSubscriber(
|
||||||
private val domainEventSubscriber: DomainEventSubscriber,
|
private val domainEventSubscriber: DomainEventSubscriber,
|
||||||
private val userRegisterHomeTimelineApplicationService: UserRegisterHomeTimelineApplicationService
|
private val userRegisterHomeTimelineApplicationService: UserRegisterHomeTimelineApplicationService
|
||||||
) : Subscriber {
|
) : Subscriber, DomainEventConsumer<UserDetailEventBody> {
|
||||||
override fun init() {
|
override fun init() {
|
||||||
domainEventSubscriber.subscribe<UserDetailEventBody>(UserDetailEvent.CREATE.eventName) {
|
domainEventSubscriber.subscribe<UserDetailEventBody>(UserDetailEvent.CREATE.eventName, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun invoke(p1: DomainEvent<UserDetailEventBody>) {
|
||||||
userRegisterHomeTimelineApplicationService.execute(
|
userRegisterHomeTimelineApplicationService.execute(
|
||||||
RegisterHomeTimeline(it.body.getUserDetail().id),
|
RegisterHomeTimeline(p1.body.getUserDetail().id),
|
||||||
Anonymous
|
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.TimelineEvent
|
||||||
import dev.usbharu.hideout.core.domain.event.timeline.TimelineEventBody
|
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.model.support.principal.Anonymous
|
||||||
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class RegisterTimelineSetTimelineStoreSubscriber(
|
class RegisterTimelineSetTimelineStoreSubscriber(
|
||||||
private val domainEventSubscriber: DomainEventSubscriber,
|
private val domainEventSubscriber: DomainEventSubscriber,
|
||||||
private val setTimelineToTimelineStoreApplicationService: SetTimelineToTimelineStoreApplicationService
|
private val setTimelineToTimelineStoreApplicationService: SetTimelineToTimelineStoreApplicationService
|
||||||
) : Subscriber {
|
) : Subscriber, DomainEventConsumer<TimelineEventBody> {
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
domainEventSubscriber.subscribe<TimelineEventBody>(TimelineEvent.CREATE.eventName) {
|
domainEventSubscriber.subscribe<TimelineEventBody>(TimelineEvent.CREATE.eventName, this)
|
||||||
setTimelineToTimelineStoreApplicationService.execute(SetTimleineStore(it.body.getTimelineId()), Anonymous)
|
}
|
||||||
}
|
|
||||||
|
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
|
package dev.usbharu.hideout.core.application.domainevent.subscribers
|
||||||
|
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.boot.ApplicationArguments
|
import org.springframework.boot.ApplicationArguments
|
||||||
import org.springframework.boot.ApplicationRunner
|
import org.springframework.boot.ApplicationRunner
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
@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?) {
|
override fun run(args: ApplicationArguments?) {
|
||||||
subscribers.forEach { it.init() }
|
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.PostEvent
|
||||||
import dev.usbharu.hideout.core.domain.event.post.PostEventBody
|
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.model.support.principal.Anonymous
|
||||||
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class TimelinePostCreateSubscriber(
|
class TimelinePostCreateSubscriber(
|
||||||
private val timelineAddPostApplicationService: TimelineAddPostApplicationService,
|
private val timelineAddPostApplicationService: TimelineAddPostApplicationService,
|
||||||
private val domainEventSubscriber: DomainEventSubscriber,
|
private val domainEventSubscriber: DomainEventSubscriber,
|
||||||
) : Subscriber {
|
) : Subscriber, DomainEventConsumer<PostEventBody> {
|
||||||
override fun init() {
|
override fun init() {
|
||||||
domainEventSubscriber.subscribe<PostEventBody>(PostEvent.CREATE.eventName) {
|
domainEventSubscriber.subscribe<PostEventBody>(PostEvent.CREATE.eventName, this)
|
||||||
timelineAddPostApplicationService.execute(AddPost(it.body.getPostId()), Anonymous)
|
}
|
||||||
}
|
|
||||||
|
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.event.relationship.RelationshipEventBody
|
||||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
|
import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||||
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@ -15,16 +16,19 @@ class TimelineRelationshipFollowSubscriber(
|
||||||
private val userAddTimelineRelationshipApplicationService: UserAddTimelineRelationshipApplicationService,
|
private val userAddTimelineRelationshipApplicationService: UserAddTimelineRelationshipApplicationService,
|
||||||
private val userDetailRepository: UserDetailRepository,
|
private val userDetailRepository: UserDetailRepository,
|
||||||
private val domainEventSubscriber: DomainEventSubscriber
|
private val domainEventSubscriber: DomainEventSubscriber
|
||||||
) : Subscriber {
|
) : Subscriber, DomainEventConsumer<RelationshipEventBody> {
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.ACCEPT_FOLLOW.eventName) {
|
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.ACCEPT_FOLLOW.eventName, this)
|
||||||
val relationship = it.body.getRelationship()
|
}
|
||||||
|
|
||||||
|
override suspend fun invoke(p1: DomainEvent<RelationshipEventBody>) {
|
||||||
|
val relationship = p1.body.getRelationship()
|
||||||
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id)
|
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id)
|
||||||
?: throw InternalServerException("Userdetail ${relationship.actorId} not found by actorid.")
|
?: throw InternalServerException("Userdetail ${relationship.actorId} not found by actorid.")
|
||||||
if (userDetail.homeTimelineId == null) {
|
if (userDetail.homeTimelineId == null) {
|
||||||
logger.warn("Home timeline for ${relationship.actorId} is not found")
|
logger.warn("Home timeline for ${relationship.actorId} is not found")
|
||||||
return@subscribe
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UnsafeCallOnNullableType")
|
@Suppress("UnsafeCallOnNullableType")
|
||||||
|
@ -34,10 +38,9 @@ class TimelineRelationshipFollowSubscriber(
|
||||||
relationship.targetActorId,
|
relationship.targetActorId,
|
||||||
Visible.FOLLOWERS
|
Visible.FOLLOWERS
|
||||||
),
|
),
|
||||||
it.body.principal
|
p1.body.principal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(TimelineRelationshipFollowSubscriber::class.java)
|
private val logger = LoggerFactory.getLogger(TimelineRelationshipFollowSubscriber::class.java)
|
||||||
|
|
|
@ -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.event.relationship.RelationshipEventBody
|
||||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository
|
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||||
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@ -15,16 +16,19 @@ class TimelineRelationshipUnfollowSubscriber(
|
||||||
private val userRemoveTimelineRelationshipApplicationService: UserRemoveTimelineRelationshipApplicationService,
|
private val userRemoveTimelineRelationshipApplicationService: UserRemoveTimelineRelationshipApplicationService,
|
||||||
private val userDetailRepository: UserDetailRepository,
|
private val userDetailRepository: UserDetailRepository,
|
||||||
private val timelineRelationshipRepository: TimelineRelationshipRepository,
|
private val timelineRelationshipRepository: TimelineRelationshipRepository,
|
||||||
) : Subscriber {
|
) : Subscriber, DomainEventConsumer<RelationshipEventBody> {
|
||||||
override fun init() {
|
override fun init() {
|
||||||
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.UNFOLLOW.eventName) {
|
domainEventSubscriber.subscribe<RelationshipEventBody>(RelationshipEvent.UNFOLLOW.eventName, this)
|
||||||
val relationship = it.body.getRelationship()
|
}
|
||||||
|
|
||||||
|
override suspend fun invoke(p1: DomainEvent<RelationshipEventBody>) {
|
||||||
|
val relationship = p1.body.getRelationship()
|
||||||
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id) ?: throw IllegalStateException(
|
val userDetail = userDetailRepository.findByActorId(relationship.actorId.id) ?: throw IllegalStateException(
|
||||||
"UserDetail ${relationship.actorId} not found by actorId."
|
"UserDetail ${relationship.actorId} not found by actorId."
|
||||||
)
|
)
|
||||||
if (userDetail.homeTimelineId == null) {
|
if (userDetail.homeTimelineId == null) {
|
||||||
logger.warn("HomeTimeline for ${userDetail.id} not found.")
|
logger.warn("HomeTimeline for ${userDetail.id} not found.")
|
||||||
return@subscribe
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val timelineRelationship = timelineRelationshipRepository.findByTimelineIdAndActorId(
|
val timelineRelationship = timelineRelationshipRepository.findByTimelineIdAndActorId(
|
||||||
|
@ -35,10 +39,9 @@ class TimelineRelationshipUnfollowSubscriber(
|
||||||
|
|
||||||
userRemoveTimelineRelationshipApplicationService.execute(
|
userRemoveTimelineRelationshipApplicationService.execute(
|
||||||
RemoveTimelineRelationship(timelineRelationship.id),
|
RemoveTimelineRelationship(timelineRelationship.id),
|
||||||
it.body.principal
|
p1.body.principal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(TimelineRelationshipUnfollowSubscriber::class.java)
|
private val logger = LoggerFactory.getLogger(TimelineRelationshipUnfollowSubscriber::class.java)
|
||||||
|
|
|
@ -19,6 +19,7 @@ package dev.usbharu.hideout.core.application.shared
|
||||||
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.MDC
|
||||||
|
|
||||||
abstract class AbstractApplicationService<T : Any, R>(
|
abstract class AbstractApplicationService<T : Any, R>(
|
||||||
protected val transaction: Transaction,
|
protected val transaction: Transaction,
|
||||||
|
@ -26,6 +27,7 @@ abstract class AbstractApplicationService<T : Any, R>(
|
||||||
) : ApplicationService<T, R> {
|
) : ApplicationService<T, R> {
|
||||||
override suspend fun execute(command: T, principal: Principal): R {
|
override suspend fun execute(command: T, principal: Principal): R {
|
||||||
return try {
|
return try {
|
||||||
|
MDC.put("applicationService", this::class.simpleName)
|
||||||
logger.debug("START {}", command::class.simpleName)
|
logger.debug("START {}", command::class.simpleName)
|
||||||
val response = transaction.transaction<R> {
|
val response = transaction.transaction<R> {
|
||||||
internalExecute(command, principal)
|
internalExecute(command, principal)
|
||||||
|
@ -39,6 +41,8 @@ abstract class AbstractApplicationService<T : Any, R>(
|
||||||
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
|
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
|
||||||
logger.warn("Command execution error", e)
|
logger.warn("Command execution error", e)
|
||||||
throw e
|
throw e
|
||||||
|
} finally {
|
||||||
|
MDC.remove("applicationService")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,11 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.config
|
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.core.infrastructure.springframework.SPAInterceptor
|
||||||
import dev.usbharu.hideout.generate.JsonOrFormModelMethodProcessor
|
import dev.usbharu.hideout.generate.JsonOrFormModelMethodProcessor
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.http.converter.HttpMessageConverter
|
import org.springframework.http.converter.HttpMessageConverter
|
||||||
|
@ -30,7 +33,8 @@ import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttribu
|
||||||
@Configuration
|
@Configuration
|
||||||
class MvcConfigurer(
|
class MvcConfigurer(
|
||||||
private val jsonOrFormModelMethodProcessor: JsonOrFormModelMethodProcessor,
|
private val jsonOrFormModelMethodProcessor: JsonOrFormModelMethodProcessor,
|
||||||
private val spaInterceptor: SPAInterceptor
|
private val spaInterceptor: SPAInterceptor,
|
||||||
|
private val applicationRequestLogInterceptor: ApplicationRequestLogInterceptor
|
||||||
) : WebMvcConfigurer {
|
) : WebMvcConfigurer {
|
||||||
override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
|
override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
|
||||||
resolvers.add(jsonOrFormModelMethodProcessor)
|
resolvers.add(jsonOrFormModelMethodProcessor)
|
||||||
|
@ -38,6 +42,16 @@ class MvcConfigurer(
|
||||||
|
|
||||||
override fun addInterceptors(registry: InterceptorRegistry) {
|
override fun addInterceptors(registry: InterceptorRegistry) {
|
||||||
registry.addInterceptor(spaInterceptor)
|
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.DomainEvent
|
||||||
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
|
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
|
||||||
|
import dev.usbharu.hideout.core.infrastructure.springframework.ApplicationRequestLogInterceptor
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.slf4j.MDC
|
||||||
import org.springframework.context.ApplicationEventPublisher
|
import org.springframework.context.ApplicationEventPublisher
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@ -27,10 +29,19 @@ class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher:
|
||||||
DomainEventPublisher {
|
DomainEventPublisher {
|
||||||
override suspend fun publishEvent(domainEvent: DomainEvent<*>) {
|
override suspend fun publishEvent(domainEvent: DomainEvent<*>) {
|
||||||
logger.trace("Publish ${domainEvent.id} ${domainEvent.name}")
|
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 {
|
companion object {
|
||||||
private val logger = LoggerFactory.getLogger(SpringFrameworkDomainEventPublisher::class.java)
|
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.DomainEventConsumer
|
||||||
import dev.usbharu.hideout.core.application.domainevent.subscribers.DomainEventSubscriber
|
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.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.LoggerFactory
|
||||||
|
import org.slf4j.MDC
|
||||||
import org.springframework.context.event.EventListener
|
import org.springframework.context.event.EventListener
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@ -17,16 +22,31 @@ class SpringFrameworkDomainEventSubscriber : DomainEventSubscriber {
|
||||||
map.getOrPut(eventName) { mutableListOf() }.add(domainEventConsumer as DomainEventConsumer<*>)
|
map.getOrPut(eventName) { mutableListOf() }.add(domainEventConsumer as DomainEventConsumer<*>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getSubscribers(): Map<String, List<DomainEventConsumer<*>>> = map
|
||||||
|
|
||||||
@EventListener
|
@EventListener
|
||||||
suspend fun onDomainEventPublished(domainEvent: DomainEvent<*>) {
|
suspend fun onDomainEventPublished(domainEvent: SpringDomainEvent) {
|
||||||
logger.trace("Domain Event Published: $domainEvent")
|
logger.debug(
|
||||||
map[domainEvent.name]?.forEach {
|
"Domain Event Published: {} id: {} requestId: {}",
|
||||||
|
domainEvent.domainEvent.name,
|
||||||
|
domainEvent.domainEvent.id,
|
||||||
|
domainEvent.requestId
|
||||||
|
)
|
||||||
|
coroutineScope {
|
||||||
|
map[domainEvent.domainEvent.name]?.map {
|
||||||
|
async(MDCContext()) {
|
||||||
try {
|
try {
|
||||||
it.invoke(domainEvent)
|
MDC.put(requestId, domainEvent.requestId)
|
||||||
|
it.invoke(domainEvent.domainEvent)
|
||||||
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
|
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
|
||||||
logger.error("", e)
|
logger.warn("", e)
|
||||||
|
null
|
||||||
|
} finally {
|
||||||
|
MDC.remove(requestId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}?.awaitAll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -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.actor.ActorRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.slf4j.MDCContext
|
||||||
import org.springframework.security.core.userdetails.UserDetails
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService
|
import org.springframework.security.core.userdetails.UserDetailsService
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||||
|
@ -33,7 +34,7 @@ class UserDetailsServiceImpl(
|
||||||
private val applicationConfig: ApplicationConfig,
|
private val applicationConfig: ApplicationConfig,
|
||||||
private val transaction: Transaction,
|
private val transaction: Transaction,
|
||||||
) : UserDetailsService {
|
) : UserDetailsService {
|
||||||
override fun loadUserByUsername(username: String?): UserDetails = runBlocking {
|
override fun loadUserByUsername(username: String?): UserDetails = runBlocking(MDCContext()) {
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
throw UsernameNotFoundException("Username not found")
|
throw UsernameNotFoundException("Username not found")
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,4 +12,8 @@
|
||||||
<appender-ref ref="ECS_JSON_FILE"/>
|
<appender-ref ref="ECS_JSON_FILE"/>
|
||||||
<appender-ref ref="FILE"/>
|
<appender-ref ref="FILE"/>
|
||||||
</root>
|
</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>
|
</configuration>
|
|
@ -1,6 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
|
@ -8,6 +8,4 @@ class MastodonReadTimeline(
|
||||||
val localOnly: Boolean,
|
val localOnly: Boolean,
|
||||||
val remoteOnly: Boolean,
|
val remoteOnly: Boolean,
|
||||||
val page: Page
|
val page: Page
|
||||||
) {
|
)
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -38,7 +38,8 @@ class MastodonReadTimelineApplicationService(
|
||||||
|
|
||||||
val readTimeline = timelineStore.readTimeline(timeline, readTimelineOption, command.page, principal)
|
val readTimeline = timelineStore.readTimeline(timeline, readTimelineOption, command.page, principal)
|
||||||
|
|
||||||
return PaginationList(readTimeline.map {
|
return PaginationList(
|
||||||
|
readTimeline.map {
|
||||||
Status(
|
Status(
|
||||||
it.postId.id.toString(),
|
it.postId.id.toString(),
|
||||||
it.post.url.toString(),
|
it.post.url.toString(),
|
||||||
|
@ -103,7 +104,10 @@ class MastodonReadTimelineApplicationService(
|
||||||
pinned = false,
|
pinned = false,
|
||||||
filtered = emptyList(),
|
filtered = emptyList(),
|
||||||
)
|
)
|
||||||
}, readTimeline.next?.id, readTimeline.prev?.id)
|
},
|
||||||
|
readTimeline.next?.id,
|
||||||
|
readTimeline.prev?.id
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -49,6 +49,7 @@ import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.mapNotNull
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.slf4j.MDCContext
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
|
|
||||||
|
@ -164,7 +165,7 @@ class SpringAccountApi(
|
||||||
id: List<String>?,
|
id: List<String>?,
|
||||||
withSuspended: Boolean
|
withSuspended: Boolean
|
||||||
): ResponseEntity<Flow<Relationship>> {
|
): 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 })
|
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.Flow
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.slf4j.MDCContext
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ class SpringTimelineApi(
|
||||||
sinceId: String?,
|
sinceId: String?,
|
||||||
minId: String?,
|
minId: String?,
|
||||||
limit: Int?
|
limit: Int?
|
||||||
): ResponseEntity<Flow<Status>> = runBlocking {
|
): ResponseEntity<Flow<Status>> = runBlocking(MDCContext()) {
|
||||||
val principal = principalContextHolder.getPrincipal()
|
val principal = principalContextHolder.getPrincipal()
|
||||||
val userDetail = transaction.transaction {
|
val userDetail = transaction.transaction {
|
||||||
userDetailRepository.findByActorId(principal.actorId.id)
|
userDetailRepository.findByActorId(principal.actorId.id)
|
||||||
|
@ -67,7 +68,8 @@ class SpringTimelineApi(
|
||||||
minId?.toLongOrNull(),
|
minId?.toLongOrNull(),
|
||||||
limit
|
limit
|
||||||
)
|
)
|
||||||
), principal
|
),
|
||||||
|
principal
|
||||||
).asFlow()
|
).asFlow()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue