This commit is contained in:
usbharu 2024-06-02 17:49:59 +09:00
parent 922bdb4991
commit ccd089fa8e
80 changed files with 523 additions and 1155 deletions

View File

@ -17,22 +17,21 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import org.springframework.stereotype.Service
@Service
class DeleteLocalActorApplicationService(
private val transaction: Transaction,
private val actor2Repository: Actor2Repository,
private val actorRepository: ActorRepository,
) {
suspend fun delete(actorId: Long, executor: ActorId) {
transaction.transaction {
val id = ActorId(actorId)
val findById = actor2Repository.findById(id)!!
val findById = actorRepository.findById(id)!!
findById.delete()
actor2Repository.delete(findById)
actorRepository.delete(findById)
}
}
}
}

View File

@ -17,8 +17,8 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.*
import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService
import org.springframework.stereotype.Service
@ -26,24 +26,23 @@ import org.springframework.stereotype.Service
@Service
class MigrationLocalActorApplicationService(
private val transaction: Transaction,
private val actor2Repository: Actor2Repository,
private val actorRepository: ActorRepository,
private val localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService,
) {
suspend fun migration(from: Long, to: Long, executor: ActorId) {
transaction.transaction<Unit> {
val fromActorId = ActorId(from)
val toActorId = ActorId(to)
val fromActor = actor2Repository.findById(fromActorId)!!
val toActor = actor2Repository.findById(toActorId)!!
val fromActor = actorRepository.findById(fromActorId)!!
val toActor = actorRepository.findById(toActorId)!!
val canAccountMigration = localActorMigrationCheckDomainService.canAccountMigration(fromActor, toActor)
when (canAccountMigration) {
is AlreadyMoved -> TODO()
is CanAccountMigration -> {
fromActor.moveTo = toActorId
actor2Repository.save(fromActor)
actorRepository.save(fromActor)
}
is CircularReferences -> TODO()
@ -51,6 +50,5 @@ class MigrationLocalActorApplicationService(
is AlsoKnownAsNotFound -> TODO()
}
}
}
}
}

View File

@ -19,22 +19,22 @@ package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.application.service.id.IdGenerateService
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorDomainService
import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService
import dev.usbharu.hideout.core.infrastructure.factory.Actor2FactoryImpl
import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl
import org.springframework.stereotype.Service
@Service
class RegisterLocalActorApplicationService(
private val transaction: Transaction,
private val actorDomainService: LocalActorDomainService,
private val actor2Repository: Actor2Repository,
private val actor2FactoryImpl: Actor2FactoryImpl,
private val actorRepository: ActorRepository,
private val actorFactoryImpl: ActorFactoryImpl,
private val instanceRepository: InstanceRepository,
private val applicationConfig: ApplicationConfig,
private val userDetailDomainService: UserDetailDomainService,
@ -44,26 +44,23 @@ class RegisterLocalActorApplicationService(
suspend fun register(registerLocalActor: RegisterLocalActor) {
transaction.transaction {
if (actorDomainService.usernameAlreadyUse(registerLocalActor.name)) {
//todo 適切な例外を考える
// todo 適切な例外を考える
throw Exception("Username already exists")
}
val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())!!
val actor = actor2FactoryImpl.createLocal(
val actor = actorFactoryImpl.createLocal(
registerLocalActor.name,
actorDomainService.generateKeyPair(),
instance.id
)
actor2Repository.save(actor)
actorRepository.save(actor)
val userDetail = UserDetail.create(
id = UserDetailId(idGenerateService.generateId()),
actorId = actor.id,
password = userDetailDomainService.hashPassword(registerLocalActor.password),
)
userDetailRepository.save(userDetail)
}
}
}
}

View File

@ -5,4 +5,4 @@ import org.springframework.stereotype.Service
@Service
interface SetAlsoKnownAsLocalActorApplicationService {
suspend fun setAlsoKnownAs(actorId: Long, alsoKnownAs: List<Long>)
}
}

View File

@ -17,24 +17,21 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import org.springframework.stereotype.Service
@Service
class SuspendLocalActorApplicationService(
private val transaction: Transaction,
private val actor2Repository: Actor2Repository,
private val actorRepository: ActorRepository,
) {
suspend fun suspend(actorId: Long, executor: ActorId) {
transaction.transaction {
val id = ActorId(actorId)
val findById = actor2Repository.findById(id)!!
val findById = actorRepository.findById(id)!!
findById.suspend = true
}
}
}
}

View File

@ -17,21 +17,20 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import org.springframework.stereotype.Service
@Service
class UnsuspendLocalActorApplicationService(
private val transaction: Transaction,
private val actor2Repository: Actor2Repository,
private val actorRepository: ActorRepository,
) {
suspend fun unsuspend(actorId: Long, executor: Long) {
transaction.transaction {
val findById = actor2Repository.findById(ActorId(actorId))!!
val findById = actorRepository.findById(ActorId(actorId))!!
findById.suspend = false
}
}
}
}

View File

@ -16,15 +16,15 @@
package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.domain.model.post.Post2Repository
import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostRepository
import org.springframework.stereotype.Service
@Service
class DeleteLocalPostApplicationService(private val postRepository: Post2Repository) {
class DeleteLocalPostApplicationService(private val postRepository: PostRepository) {
suspend fun delete(postId: Long) {
val findById = postRepository.findById(PostId(postId))!!
findById.delete()
postRepository.save(findById)
}
}
}

View File

@ -16,27 +16,26 @@
package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.Post2Repository
import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostOverview
import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl
import org.springframework.stereotype.Service
@Service
class RegisterLocalPostApplicationService(
private val postFactory: PostFactoryImpl,
private val actor2Repository: Actor2Repository,
private val postRepository: Post2Repository,
private val actorRepository: ActorRepository,
private val postRepository: PostRepository,
) {
suspend fun register(registerLocalPost: RegisterLocalPost) {
val actorId = ActorId(registerLocalPost.actorId)
val post = postFactory.createLocal(
actorId,
actor2Repository.findById(actorId)!!.name,
actorRepository.findById(actorId)!!.name,
PostOverview(registerLocalPost.overview),
registerLocalPost.content,
registerLocalPost.visibility,
@ -48,4 +47,4 @@ class RegisterLocalPostApplicationService(
postRepository.save(post)
}
}
}

View File

@ -18,16 +18,16 @@ package dev.usbharu.hideout.core.application.post
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.Post2Repository
import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostOverview
import dev.usbharu.hideout.core.domain.model.post.PostRepository
import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl
import org.springframework.stereotype.Service
@Service
class UpdateLocalNoteApplicationService(
private val transaction: Transaction,
private val postRepository: Post2Repository,
private val postRepository: PostRepository,
private val postContentFactoryImpl: PostContentFactoryImpl,
) {
suspend fun update(updateLocalNote: UpdateLocalNote) {
@ -42,4 +42,4 @@ class UpdateLocalNoteApplicationService(
postRepository.save(post)
}
}
}
}

View File

@ -16,11 +16,11 @@
package dev.usbharu.hideout.core.domain.event.actor
import dev.usbharu.hideout.core.domain.model.actor.Actor2
import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
class ActorDomainEventFactory(private val actor: Actor2) {
class ActorDomainEventFactory(private val actor: Actor) {
fun createEvent(actorEvent: ActorEvent): DomainEvent {
return DomainEvent.create(
actorEvent.eventName,
@ -30,7 +30,7 @@ class ActorDomainEventFactory(private val actor: Actor2) {
}
}
class ActorEventBody(actor: Actor2) : DomainEventBody(
class ActorEventBody(actor: Actor) : DomainEventBody(
mapOf(
"actor" to actor
)
@ -43,4 +43,4 @@ enum class ActorEvent(val eventName: String, val collectable: Boolean = true) {
move("ActorMove"),
actorSuspend("ActorSuspend"),
actorUnsuspend("ActorUnsuspend"),
}
}

View File

@ -44,4 +44,4 @@ enum class ActorInstanceRelationshipEvent(val eventName: String) {
block("ActorInstanceBlock"),
mute("ActorInstanceMute"),
unmute("ActorInstanceUnmute"),
}
}

View File

@ -33,4 +33,4 @@ class InstanceEventBody(instance: Instance) : DomainEventBody(mapOf("instance" t
enum class InstanceEvent(val eventName: String) {
update("InstanceUpdate")
}
}

View File

@ -16,11 +16,11 @@
package dev.usbharu.hideout.core.domain.event.post
import dev.usbharu.hideout.core.domain.model.post.Post2
import dev.usbharu.hideout.core.domain.model.post.Post
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
class PostDomainEventFactory(private val post: Post2) {
class PostDomainEventFactory(private val post: Post) {
fun createEvent(postEvent: PostEvent): DomainEvent {
return DomainEvent.create(
postEvent.eventName,
@ -29,11 +29,11 @@ class PostDomainEventFactory(private val post: Post2) {
}
}
class PostEventBody(post: Post2) : DomainEventBody(mapOf("post" to post))
class PostEventBody(post: Post) : DomainEventBody(mapOf("post" to post))
enum class PostEvent(val eventName: String) {
delete("PostDelete"),
update("PostUpdate"),
create("PostCreate"),
checkUpdate("PostCheckUpdate"),
}
}

View File

@ -16,17 +16,17 @@
package dev.usbharu.hideout.core.domain.event.relationship
import dev.usbharu.hideout.core.domain.model.relationship.Relationship2
import dev.usbharu.hideout.core.domain.model.relationship.Relationship
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
class RelationshipEventFactory(private val relationship2: Relationship2) {
class RelationshipEventFactory(private val relationship: Relationship) {
fun createEvent(relationshipEvent: RelationshipEvent): DomainEvent {
return DomainEvent.create(relationshipEvent.eventName, RelationshipEventBody(relationship2))
return DomainEvent.create(relationshipEvent.eventName, RelationshipEventBody(relationship))
}
}
class RelationshipEventBody(relationship: Relationship2) : DomainEventBody(mapOf("relationship" to relationship))
class RelationshipEventBody(relationship: Relationship) : DomainEventBody(mapOf("relationship" to relationship))
enum class RelationshipEvent(val eventName: String) {
follow("RelationshipFollow"),
@ -37,4 +37,4 @@ enum class RelationshipEvent(val eventName: String) {
unmute("RelationshipUnmute"),
followRequest("RelationshipFollowRequest"),
unfollowRequest("RelationshipUnfollowRequest"),
}
}

View File

@ -18,13 +18,14 @@ package dev.usbharu.hideout.core.domain.model.actor
import dev.usbharu.hideout.core.domain.event.actor.ActorDomainEventFactory
import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.*
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.shared.Domain
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
import java.net.URI
import java.time.Instant
class Actor2 private constructor(
class Actor(
val id: ActorId,
val name: ActorName,
val domain: Domain,
@ -49,6 +50,8 @@ class Actor2 private constructor(
var lastUpdateAt: Instant = createdAt,
alsoKnownAs: Set<ActorId> = emptySet(),
moveTo: ActorId? = null,
emojiIds: Set<EmojiId>,
deleted: Boolean,
) : DomainEventStorable() {
var suspend = suspend
@ -74,9 +77,8 @@ class Actor2 private constructor(
field = value
}
val emojis
get() = screenName.emojis + description.emojis
var emojis = emojiIds
private set
var description = description
set(value) {
@ -89,62 +91,28 @@ class Actor2 private constructor(
field = value
}
var deleted = deleted
private set
fun delete() {
addDomainEvent(ActorDomainEventFactory(this).createEvent(delete))
if (deleted.not()) {
addDomainEvent(ActorDomainEventFactory(this).createEvent(delete))
screenName = ActorScreenName.empty
description = ActorDescription.empty
emojis = emptySet()
lastPostAt = null
postsCount = ActorPostsCount.ZERO
followersCount = null
followingCount = null
}
}
fun restore() {
deleted = false
checkUpdate()
}
fun checkUpdate() {
addDomainEvent(ActorDomainEventFactory(this).createEvent(checkUpdate))
}
abstract class Actor2Factory {
protected suspend fun internalCreate(
id: ActorId,
name: ActorName,
domain: Domain,
screenName: ActorScreenName,
description: ActorDescription,
inbox: URI,
outbox: URI,
url: URI,
publicKey: ActorPublicKey,
privateKey: ActorPrivateKey? = null,
createdAt: Instant,
keyId: ActorKeyId,
followersEndpoint: URI,
followingEndpoint: URI,
instance: InstanceId,
locked: Boolean,
followersCount: ActorRelationshipCount,
followingCount: ActorRelationshipCount,
postsCount: ActorPostsCount,
lastPostDate: Instant? = null,
suspend: Boolean,
): Actor2 {
return Actor2(
id = id,
name = name,
domain = domain,
screenName = screenName,
description = description,
inbox = inbox,
outbox = outbox,
url = url,
publicKey = publicKey,
privateKey = privateKey,
createdAt = createdAt,
keyId = keyId,
followersEndpoint = followersEndpoint,
followingEndpoint = followingEndpoint,
instance = instance,
locked = locked,
followersCount = followersCount,
followingCount = followingCount,
postsCount = postsCount,
lastPostAt = lastPostDate,
suspend = suspend
)
}
}
}

View File

@ -16,15 +16,10 @@
package dev.usbharu.hideout.core.domain.model.actor
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
class ActorDescription private constructor(val description: String, val emojis: List<EmojiId>) {
@JvmInline
value class ActorDescription(val description: String) {
companion object {
val length = 10000
val empty = ActorDescription("")
}
abstract class ActorDescriptionFactory {
protected suspend fun create(description: String, emojis: List<EmojiId>): ActorDescription =
ActorDescription(description, emojis)
}
}
}

View File

@ -24,4 +24,4 @@ value class ActorId(val id: Long) {
companion object {
val ghost = ActorId(0L)
}
}
}

View File

@ -17,4 +17,4 @@
package dev.usbharu.hideout.core.domain.model.actor
@JvmInline
value class ActorKeyId(val keyId: String)
value class ActorKeyId(val keyId: String)

View File

@ -18,11 +18,8 @@ package dev.usbharu.hideout.core.domain.model.actor
@JvmInline
value class ActorName(val name: String) {
init {
}
companion object {
val length = 300
}
}
}

View File

@ -24,4 +24,8 @@ value class ActorPostsCount(val postsCount: Int) {
operator fun inc() = ActorPostsCount(postsCount + 1)
operator fun dec() = ActorPostsCount(postsCount - 1)
}
companion object {
val ZERO = ActorPostsCount(0)
}
}

View File

@ -17,4 +17,4 @@
package dev.usbharu.hideout.core.domain.model.actor
@JvmInline
value class ActorPrivateKey(val privateKey: String)
value class ActorPrivateKey(val privateKey: String)

View File

@ -17,4 +17,4 @@
package dev.usbharu.hideout.core.domain.model.actor
@JvmInline
value class ActorPublicKey(val publicKey: String)
value class ActorPublicKey(val publicKey: String)

View File

@ -24,4 +24,4 @@ value class ActorRelationshipCount(val relationshipCount: Int) {
operator fun inc(): ActorRelationshipCount = ActorRelationshipCount(relationshipCount + 1)
operator fun dec(): ActorRelationshipCount = ActorRelationshipCount(relationshipCount - 1)
}
}

View File

@ -16,8 +16,8 @@
package dev.usbharu.hideout.core.domain.model.actor
interface Actor2Repository {
suspend fun save(actor: Actor2): Actor2
suspend fun delete(actor: Actor2)
suspend fun findById(id: ActorId): Actor2?
}
interface ActorRepository {
suspend fun save(actor: Actor): Actor
suspend fun delete(actor: Actor)
suspend fun findById(id: ActorId): Actor?
}

View File

@ -16,16 +16,10 @@
package dev.usbharu.hideout.core.domain.model.actor
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
class ActorScreenName private constructor(val screenName: String, val emojis: List<EmojiId>) {
@JvmInline
value class ActorScreenName(val screenName: String) {
companion object {
val length = 300
}
abstract class ActorScreenNameFactory {
protected suspend fun create(screenName: String, emojis: List<EmojiId>): ActorScreenName =
ActorScreenName(screenName, emojis)
val empty = ActorScreenName("")
}
}

View File

@ -85,4 +85,4 @@ data class ActorInstanceRelationship(
result = 31 * result + instanceId.hashCode()
return result
}
}
}

View File

@ -19,4 +19,4 @@ package dev.usbharu.hideout.core.domain.model.actorinstancerelationship
interface ActorInstanceRelationshipRepository {
suspend fun save(actorInstanceRelationship: ActorInstanceRelationship): ActorInstanceRelationship
suspend fun delete(actorInstanceRelationship: ActorInstanceRelationship)
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.domain.model.deletedActor
import dev.usbharu.hideout.core.domain.model.actor.ActorName
import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey
import dev.usbharu.hideout.core.domain.model.shared.Domain
import java.net.URI
import java.time.Instant
data class DeletedActor(
val id: DeletedActorId,
val name: ActorName,
val domain: Domain,
val apId: URI,
val publicKey: ActorPublicKey,
val deletedAt: Instant,
)

View File

@ -1,20 +0,0 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.domain.model.deletedActor
@JvmInline
value class DeletedActorId(val id: Long)

View File

@ -1,24 +0,0 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.domain.model.deletedActor
interface DeletedActorRepository {
suspend fun save(deletedActor: DeletedActor): DeletedActor
suspend fun delete(deletedActor: DeletedActor)
suspend fun findById(id: DeletedActorId): DeletedActor?
suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor?
}

View File

@ -17,10 +17,8 @@
package dev.usbharu.hideout.core.domain.model.emoji
interface CustomEmojiRepository {
suspend fun generateId(): Long
suspend fun save(customEmoji: CustomEmoji): CustomEmoji
suspend fun findById(id: Long): CustomEmoji?
suspend fun delete(customEmoji: CustomEmoji)
suspend fun findByNameAndDomain(name: String, domain: String): CustomEmoji?
suspend fun findByNamesAndDomain(names: List<String>, domain: String): List<CustomEmoji>
}

View File

@ -17,4 +17,4 @@
package dev.usbharu.hideout.core.domain.model.emoji
@JvmInline
value class EmojiId(val emojiId: Long)
value class EmojiId(val emojiId: Long)

View File

@ -37,7 +37,6 @@ class Instance(
val createdAt: Instant,
) : DomainEventStorable() {
var iconUrl = iconUrl
set(value) {
addDomainEvent(InstanceEventFactory(this).createEvent(InstanceEvent.update))
@ -56,6 +55,4 @@ class Instance(
override fun hashCode(): Int {
return id.hashCode()
}
}

View File

@ -17,4 +17,4 @@
package dev.usbharu.hideout.core.domain.model.instance
@JvmInline
value class InstanceId(val instanceId: Long)
value class InstanceId(val instanceId: Long)

View File

@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.domain.model.instance
import java.net.URI
interface InstanceRepository {
suspend fun generateId(): InstanceId
suspend fun save(instance: Instance): Instance
suspend fun findById(id: InstanceId): Instance?
suspend fun delete(instance: Instance)

View File

@ -29,4 +29,3 @@ data class Media(
val blurHash: MediaBlurHash?,
val description: MediaDescription? = null,
)

View File

@ -17,4 +17,4 @@
package dev.usbharu.hideout.core.domain.model.media
@JvmInline
value class MediaBlurHash(val hash: String)
value class MediaBlurHash(val hash: String)

View File

@ -17,9 +17,7 @@
package dev.usbharu.hideout.core.domain.model.media
interface MediaRepository {
suspend fun generateId(): Long
suspend fun save(media: Media): Media
suspend fun findById(id: Long): Media?
suspend fun delete(id: Long)
suspend fun findByRemoteUrl(remoteUrl: String): Media?
suspend fun findById(id: MediaId): Media?
suspend fun delete(media: Media)
}

View File

@ -24,7 +24,7 @@ import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
import java.net.URI
import java.time.Instant
class Post2 private constructor(
class Post private constructor(
val id: PostId,
actorId: ActorId,
overview: PostOverview? = null,
@ -143,7 +143,6 @@ class Post2 private constructor(
content = PostContent.empty
overview = null
mediaIds = emptyList()
}
deleted = true
}
@ -157,6 +156,7 @@ class Post2 private constructor(
this.content = content
this.overview = overview
this.mediaIds = mediaIds
checkUpdate()
}
var hide = hide
@ -182,7 +182,7 @@ class Post2 private constructor(
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Post2
other as Post
return id == other.id
}
@ -207,8 +207,8 @@ class Post2 private constructor(
deleted: Boolean,
mediaIds: List<MediaId>,
hide: Boolean,
): Post2 {
return Post2(
): Post {
return Post(
id = id,
actorId = actorId,
overview = overview,
@ -226,4 +226,4 @@ class Post2 private constructor(
).apply { addDomainEvent(PostDomainEventFactory(this).createEvent(PostEvent.create)) }
}
}
}
}

View File

@ -29,7 +29,9 @@ class PostContent private constructor(val text: String, val content: String, val
abstract class PostContentFactory {
protected suspend fun create(text: String, content: String, emojiIds: List<EmojiId>): PostContent {
return PostContent(
text, content, emojiIds
text,
content,
emojiIds
)
}
}

View File

@ -18,10 +18,10 @@ package dev.usbharu.hideout.core.domain.model.post
import dev.usbharu.hideout.core.domain.model.actor.ActorId
interface Post2Repository {
suspend fun save(post: Post2): Post2
suspend fun saveAll(posts: List<Post2>): List<Post2>
suspend fun findById(id: PostId): Post2?
suspend fun findByActorId(id: ActorId): List<Post2>
suspend fun delete(post: Post2)
}
interface PostRepository {
suspend fun save(post: Post): Post
suspend fun saveAll(posts: List<Post>): List<Post>
suspend fun findById(id: PostId): Post?
suspend fun findByActorId(id: ActorId): List<Post>
suspend fun delete(post: Post)
}

View File

@ -21,7 +21,7 @@ import dev.usbharu.hideout.core.domain.event.relationship.RelationshipEventFacto
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
class Relationship2(
class Relationship(
val actorId: ActorId,
val targetActorId: ActorId,
following: Boolean,
@ -43,7 +43,6 @@ class Relationship2(
var mutingFollowRequest: Boolean = mutingFollowRequest
private set
fun follow() {
require(blocking.not())
following = true
@ -103,4 +102,4 @@ class Relationship2(
fun rejectFollowRequest() {
followRequesting = false
}
}
}

View File

@ -16,7 +16,7 @@
package dev.usbharu.hideout.core.domain.model.relationship
interface Relationship2Repository {
suspend fun save(relationship2: Relationship2): Relationship2
suspend fun delete(relationship2: Relationship2)
}
interface RelationshipRepository {
suspend fun save(relationship: Relationship): Relationship
suspend fun delete(relationship: Relationship)
}

View File

@ -21,4 +21,4 @@ value class Domain(val domain: String) {
companion object {
val length = 1000
}
}
}

View File

@ -17,14 +17,14 @@
package dev.usbharu.hideout.core.domain.service.actor
import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.model.actor.Actor2
import dev.usbharu.hideout.core.domain.model.actor.Actor
import org.springframework.stereotype.Service
interface IRemoteActorCheckDomainService {
fun isRemoteActor(actor: Actor2): Boolean
fun isRemoteActor(actor: Actor): Boolean
}
@Service
class RemoteActorCheckDomainService(private val applicationConfig: ApplicationConfig) : IRemoteActorCheckDomainService {
override fun isRemoteActor(actor: Actor2): Boolean = actor.domain.domain == applicationConfig.url.host
override fun isRemoteActor(actor: Actor): Boolean = actor.domain.domain == applicationConfig.url.host
}

View File

@ -22,4 +22,4 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey
interface LocalActorDomainService {
suspend fun usernameAlreadyUse(name: String): Boolean
suspend fun generateKeyPair(): Pair<ActorPublicKey, ActorPrivateKey>
}
}

View File

@ -16,10 +16,10 @@
package dev.usbharu.hideout.core.domain.service.actor.local
import dev.usbharu.hideout.core.domain.model.actor.Actor2
import dev.usbharu.hideout.core.domain.model.actor.Actor
interface LocalActorMigrationCheckDomainService {
suspend fun canAccountMigration(from: Actor2, to: Actor2): AccountMigrationCheck
suspend fun canAccountMigration(from: Actor, to: Actor): AccountMigrationCheck
}
sealed class AccountMigrationCheck(
@ -34,4 +34,4 @@ sealed class AccountMigrationCheck(
class AlreadyMoved(val message: String) : AccountMigrationCheck(false)
class AlsoKnownAsNotFound(val message: String) : AccountMigrationCheck(false)
}
}

View File

@ -18,4 +18,4 @@ package dev.usbharu.hideout.core.domain.service.actorinstancerelationship
interface ActorInstanceRelationshipDomainService {
suspend fun block()
}
}

View File

@ -18,4 +18,4 @@ package dev.usbharu.hideout.core.domain.service.userdetail
interface PasswordEncoder {
suspend fun encode(input: String): String
}
}

View File

@ -22,4 +22,4 @@ import org.springframework.stereotype.Service
@Service
class UserDetailDomainService(private val passwordEncoder: PasswordEncoder) {
suspend fun hashPassword(password: String) = UserDetailHashedPassword(passwordEncoder.encode(password))
}
}

View File

@ -50,4 +50,4 @@ data class DomainEvent(
return DomainEvent(id, name, occurredOn, body, collectable)
}
}
}
}

View File

@ -20,4 +20,4 @@ abstract class DomainEventBody(val map: Map<String, Any>) {
fun toMap(): Map<String, Any> {
return map
}
}
}

View File

@ -2,4 +2,4 @@ package dev.usbharu.hideout.core.domain.shared.domainevent
interface DomainEventPublisher {
suspend fun publishEvent(domainEvent: DomainEvent)
}
}

View File

@ -26,4 +26,4 @@ abstract class DomainEventStorable {
fun clearDomainEvents() = domainEvents.clear()
fun getDomainEvents(): List<DomainEvent> = domainEvents.toList()
}
}

View File

@ -4,4 +4,4 @@ interface DomainEventSubscriber {
fun subscribe(eventName: String, domainEventConsumer: DomainEventConsumer)
}
typealias DomainEventConsumer = (DomainEvent) -> Unit
typealias DomainEventConsumer = (DomainEvent) -> Unit

View File

@ -19,4 +19,4 @@ interface DomainEventPublishableRepository<T : DomainEventStorable> {
}
entity.clearDomainEvents()
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.exposed
import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper
import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper
import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors
import dev.usbharu.hideout.core.infrastructure.exposedrepository.ActorsAlsoKnownAs
import org.jetbrains.exposed.sql.Query
import org.jetbrains.exposed.sql.ResultRow
import org.springframework.stereotype.Component
@Component
class ActorQueryMapper(private val actorResultRowMapper: ResultRowMapper<Actor>) : QueryMapper<Actor> {
override fun map(query: Query): List<Actor> {
return query
.groupBy { it[Actors.id] }
.map { it.value }
.map {
it
.first()
.let(actorResultRowMapper::map)
.apply {
alsoKnownAs = it.mapNotNull { resultRow: ResultRow ->
resultRow.getOrNull(
ActorsAlsoKnownAs.alsoKnownAs
)?.let { actorId ->
ActorId(
actorId
)
}
}.toSet()
clearDomainEvents()
}
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.exposed
import dev.usbharu.hideout.application.infrastructure.exposed.ResultRowMapper
import dev.usbharu.hideout.core.domain.model.actor.*
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.shared.Domain
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors
import org.jetbrains.exposed.sql.ResultRow
import org.springframework.stereotype.Component
import java.net.URI
@Component
class ActorResultRowMapper : ResultRowMapper<Actor> {
override fun map(resultRow: ResultRow): Actor {
return Actor(
id = ActorId(resultRow[Actors.id]),
name = ActorName(resultRow[Actors.name]),
domain = Domain(resultRow[Actors.domain]),
screenName = ActorScreenName(resultRow[Actors.screenName]),
description = ActorDescription(resultRow[Actors.description]),
inbox = URI.create(resultRow[Actors.inbox]),
outbox = URI.create(resultRow[Actors.outbox]),
url = URI.create(resultRow[Actors.url]),
publicKey = ActorPublicKey(resultRow[Actors.publicKey]),
privateKey = resultRow[Actors.privateKey]?.let { ActorPrivateKey(it) },
createdAt = resultRow[Actors.createdAt],
keyId = ActorKeyId(resultRow[Actors.keyId]),
followersEndpoint = resultRow[Actors.followers]?.let { URI.create(it) },
followingEndpoint = resultRow[Actors.following]?.let { URI.create(it) },
instance = InstanceId(resultRow[Actors.instance]),
locked = resultRow[Actors.locked],
followersCount = resultRow[Actors.followersCount]?.let { ActorRelationshipCount(it) },
followingCount = resultRow[Actors.followingCount]?.let { ActorRelationshipCount(it) },
postsCount = ActorPostsCount(resultRow[Actors.postsCount]),
lastPostAt = resultRow[Actors.lastPostAt],
suspend = resultRow[Actors.suspend],
lastUpdateAt = resultRow[Actors.lastUpdateAt],
alsoKnownAs = emptySet(),
moveTo = resultRow[Actors.moveTo]?.let { ActorId(it) },
emojiIds = resultRow[Actors.emojis]
.split(",")
.filter { it.isNotEmpty() }
.map { EmojiId(it.toLong()) }
.toSet(),
deleted = resultRow[Actors.deleted]
)
}
}

View File

@ -16,9 +16,11 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.application.service.id.IdGenerateService
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.shared.Domain
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.CurrentTimestamp
@ -26,34 +28,33 @@ import org.jetbrains.exposed.sql.javatime.timestamp
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
import java.net.URI
@Repository
class CustomEmojiRepositoryImpl(private val idGenerateService: IdGenerateService) : CustomEmojiRepository,
class CustomEmojiRepositoryImpl : CustomEmojiRepository,
AbstractRepository() {
override val logger: Logger
get() = Companion.logger
override suspend fun generateId(): Long = idGenerateService.generateId()
override suspend fun save(customEmoji: CustomEmoji): CustomEmoji = query {
val singleOrNull =
CustomEmojis.selectAll().where { CustomEmojis.id eq customEmoji.id }.forUpdate().singleOrNull()
CustomEmojis.selectAll().where { CustomEmojis.id eq customEmoji.id.emojiId }.forUpdate().singleOrNull()
if (singleOrNull == null) {
CustomEmojis.insert {
it[id] = customEmoji.id
it[id] = customEmoji.id.emojiId
it[name] = customEmoji.name
it[domain] = customEmoji.domain
it[instanceId] = customEmoji.instanceId
it[url] = customEmoji.url
it[domain] = customEmoji.domain.domain
it[instanceId] = customEmoji.instanceId.instanceId
it[url] = customEmoji.url.toString()
it[category] = customEmoji.category
it[createdAt] = customEmoji.createdAt
}
} else {
CustomEmojis.update({ CustomEmojis.id eq customEmoji.id }) {
CustomEmojis.update({ CustomEmojis.id eq customEmoji.id.emojiId }) {
it[name] = customEmoji.name
it[domain] = customEmoji.domain
it[instanceId] = customEmoji.instanceId
it[url] = customEmoji.url
it[domain] = customEmoji.domain.domain
it[instanceId] = customEmoji.instanceId.instanceId
it[url] = customEmoji.url.toString()
it[category] = customEmoji.category
it[createdAt] = customEmoji.createdAt
}
@ -66,14 +67,16 @@ class CustomEmojiRepositoryImpl(private val idGenerateService: IdGenerateService
}
override suspend fun delete(customEmoji: CustomEmoji): Unit = query {
CustomEmojis.deleteWhere { id eq customEmoji.id }
CustomEmojis.deleteWhere { id eq customEmoji.id.emojiId }
}
override suspend fun findByNameAndDomain(name: String, domain: String): CustomEmoji? = query {
return@query CustomEmojis
.selectAll().where { CustomEmojis.name eq name and (CustomEmojis.domain eq domain) }
.singleOrNull()
?.toCustomEmoji()
override suspend fun findByNamesAndDomain(names: List<String>, domain: String): List<CustomEmoji> {
return CustomEmojis
.selectAll()
.where {
CustomEmojis.name inList names and (CustomEmojis.domain eq domain)
}
.map { it.toCustomEmoji() }
}
companion object {
@ -82,22 +85,22 @@ class CustomEmojiRepositoryImpl(private val idGenerateService: IdGenerateService
}
fun ResultRow.toCustomEmoji(): CustomEmoji = CustomEmoji(
id = this[CustomEmojis.id],
id = EmojiId(this[CustomEmojis.id]),
name = this[CustomEmojis.name],
domain = this[CustomEmojis.domain],
instanceId = this[CustomEmojis.instanceId],
url = this[CustomEmojis.url],
domain = Domain(this[CustomEmojis.domain]),
instanceId = InstanceId(this[CustomEmojis.instanceId]),
url = URI.create(this[CustomEmojis.url]),
category = this[CustomEmojis.category],
createdAt = this[CustomEmojis.createdAt]
)
fun ResultRow.toCustomEmojiOrNull(): CustomEmoji? {
return CustomEmoji(
id = this.getOrNull(CustomEmojis.id) ?: return null,
id = EmojiId(this.getOrNull(CustomEmojis.id) ?: return null),
name = this.getOrNull(CustomEmojis.name) ?: return null,
domain = this.getOrNull(CustomEmojis.domain) ?: return null,
instanceId = this[CustomEmojis.instanceId],
url = this.getOrNull(CustomEmojis.url) ?: return null,
domain = Domain(this.getOrNull(CustomEmojis.domain) ?: return null),
instanceId = InstanceId(this.getOrNull(CustomEmojis.instanceId) ?: return null),
url = URI.create(this.getOrNull(CustomEmojis.url) ?: return null),
category = this[CustomEmojis.category],
createdAt = this.getOrNull(CustomEmojis.createdAt) ?: return null
)
@ -107,7 +110,7 @@ object CustomEmojis : Table("emojis") {
val id = long("id")
val name = varchar("name", 1000)
val domain = varchar("domain", 1000)
val instanceId = long("instance_id").references(Instance.id).nullable()
val instanceId = long("instance_id").references(Instance.id)
val url = varchar("url", 255).uniqueIndex()
val category = varchar("category", 255).nullable()
val createdAt = timestamp("created_at").defaultExpression(CurrentTimestamp)

View File

@ -1,106 +0,0 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.timestamp
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
@Repository
class DeletedActorRepositoryImpl : DeletedActorRepository, AbstractRepository() {
override val logger: Logger
get() = Companion.logger
override suspend fun save(deletedActor: DeletedActor): DeletedActor = query {
val singleOrNull =
DeletedActors.selectAll().where { DeletedActors.id eq deletedActor.id }.forUpdate().singleOrNull()
if (singleOrNull == null) {
DeletedActors.insert {
it[id] = deletedActor.id
it[name] = deletedActor.name
it[domain] = deletedActor.domain
it[apId] = deletedActor.apId
it[publicKey] = deletedActor.publicKey
it[deletedAt] = deletedActor.deletedAt
}
} else {
DeletedActors.update({ DeletedActors.id eq deletedActor.id }) {
it[name] = deletedActor.name
it[domain] = deletedActor.domain
it[apId] = deletedActor.apId
it[publicKey] = deletedActor.publicKey
it[deletedAt] = deletedActor.deletedAt
}
}
return@query deletedActor
}
override suspend fun delete(deletedActor: DeletedActor): Unit = query {
DeletedActors.deleteWhere { id eq deletedActor.id }
}
override suspend fun findById(id: Long): DeletedActor? = query {
return@query DeletedActors
.selectAll().where { DeletedActors.id eq id }
.singleOrNull()
?.toDeletedActor()
}
override suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor? = query {
return@query DeletedActors
.selectAll().where { DeletedActors.name eq name and (DeletedActors.domain eq domain) }
.singleOrNull()
?.toDeletedActor()
}
companion object {
private val logger = LoggerFactory.getLogger(DeletedActorRepositoryImpl::class.java)
}
}
fun ResultRow.toDeletedActor(): DeletedActor = deletedActor(this)
private fun deletedActor(singleOr: ResultRow): DeletedActor {
return DeletedActor(
id = singleOr[DeletedActors.id],
name = singleOr[DeletedActors.name],
domain = singleOr[DeletedActors.domain],
apId = singleOr[DeletedActors.publicKey],
publicKey = singleOr[DeletedActors.apId],
deletedAt = singleOr[DeletedActors.deletedAt]
)
}
object DeletedActors : Table("deleted_actors") {
val id = long("id")
val name = varchar("name", 300)
val domain = varchar("domain", 255)
val apId = varchar("ap_id", 255).uniqueIndex()
val publicKey = varchar("public_key", 10000).uniqueIndex()
val deletedAt = timestamp("deleted_at")
override val primaryKey: PrimaryKey = PrimaryKey(id)
init {
uniqueIndex(name, domain)
}
}

View File

@ -1,5 +1,6 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.application.infrastructure.exposed.QueryMapper
import dev.usbharu.hideout.core.domain.model.actor.*
import dev.usbharu.hideout.core.domain.model.shared.Domain
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
@ -12,18 +13,22 @@ import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
@Repository
class ExposedActor2Repository(override val domainEventPublisher: DomainEventPublisher) : AbstractRepository(),
DomainEventPublishableRepository<Actor2>, Actor2Repository {
class ExposedActorRepository(
private val actorQueryMapper: QueryMapper<Actor>,
override val domainEventPublisher: DomainEventPublisher,
) : AbstractRepository(),
DomainEventPublishableRepository<Actor>,
ActorRepository {
override val logger: Logger
get() = Companion.logger
companion object {
private val logger = LoggerFactory.getLogger(ExposedActor2Repository::class.java)
private val logger = LoggerFactory.getLogger(ExposedActorRepository::class.java)
}
override suspend fun save(actor: Actor2): Actor2 {
override suspend fun save(actor: Actor): Actor {
query {
Actors2.upsert {
Actors.upsert {
it[id] = actor.id.id
it[name] = actor.name.name
it[domain] = actor.domain.domain
@ -49,32 +54,41 @@ class ExposedActor2Repository(override val domainEventPublisher: DomainEventPubl
it[moveTo] = actor.moveTo?.id
it[emojis] = actor.emojis.joinToString(",")
}
Actors2AlsoKnownAs.deleteWhere {
ActorsAlsoKnownAs.deleteWhere {
actorId eq actor.id.id
}
Actors2AlsoKnownAs.batchInsert(actor.alsoKnownAs) {
this[Actors2AlsoKnownAs.actorId] = actor.id.id
this[Actors2AlsoKnownAs.alsoKnownAs] = it.id
ActorsAlsoKnownAs.batchInsert(actor.alsoKnownAs) {
this[ActorsAlsoKnownAs.actorId] = actor.id.id
this[ActorsAlsoKnownAs.alsoKnownAs] = it.id
}
}
update(actor)
return actor
}
override suspend fun delete(actor: Actor2) {
override suspend fun delete(actor: Actor) {
query {
Actors2.deleteWhere { id eq actor.id.id }
Actors2AlsoKnownAs.deleteWhere { actorId eq actor.id.id }
Actors.deleteWhere { id eq actor.id.id }
ActorsAlsoKnownAs.deleteWhere { actorId eq actor.id.id }
}
update(actor)
}
override suspend fun findById(id: ActorId): Actor2? {
TODO()
override suspend fun findById(id: ActorId): Actor? {
return query {
Actors
.leftJoin(ActorsAlsoKnownAs, onColumn = { Actors.id }, otherColumn = { actorId })
.selectAll()
.where {
Actors.id eq id.id
}
.let(actorQueryMapper::map)
.first()
}
}
}
object Actors2 : Table("actors") {
object Actors : Table("actors") {
val id = long("id")
val name = varchar("name", ActorName.length)
val domain = varchar("domain", Domain.length)
@ -99,6 +113,7 @@ object Actors2 : Table("actors") {
val suspend = bool("suspend")
val moveTo = long("move_to").references(id).nullable()
val emojis = varchar("emojis", 3000)
val deleted = bool("deleted")
override val primaryKey = PrimaryKey(id)
@ -107,10 +122,10 @@ object Actors2 : Table("actors") {
}
}
object Actors2AlsoKnownAs : Table("actor_alsoknwonas") {
object ActorsAlsoKnownAs : Table("actor_alsoknwonas") {
val actorId =
long("actor_id").references(Actors2.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
val alsoKnownAs = long("alsoKnownAs").references(Actors2.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE)
long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
val alsoKnownAs = long("alsoKnownAs").references(Actors.id, ReferenceOption.CASCADE, ReferenceOption.CASCADE)
override val primaryKey: PrimaryKey = PrimaryKey(actorId, alsoKnownAs)
}
}

View File

@ -1,98 +0,0 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.application.service.id.IdGenerateService
import dev.usbharu.hideout.core.domain.model.filter.FilterMode
import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword
import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeywordRepository
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
@Repository
class ExposedFilterKeywordRepository(private val idGenerateService: IdGenerateService) : FilterKeywordRepository,
AbstractRepository() {
override val logger: Logger
get() = Companion.logger
override suspend fun generateId(): Long = idGenerateService.generateId()
override suspend fun save(filterKeyword: FilterKeyword): FilterKeyword = query {
val empty = FilterKeywords.selectAll().where { FilterKeywords.id eq filterKeyword.id }.empty()
if (empty) {
FilterKeywords.insert {
it[id] = filterKeyword.id
it[filterId] = filterKeyword.filterId
it[keyword] = filterKeyword.keyword
it[mode] = filterKeyword.mode.name
}
} else {
FilterKeywords.update({ FilterKeywords.id eq filterKeyword.id }) {
it[filterId] = filterKeyword.filterId
it[keyword] = filterKeyword.keyword
it[mode] = filterKeyword.mode.name
}
}
filterKeyword
}
override suspend fun saveAll(filterKeywordList: List<FilterKeyword>): Unit = query {
FilterKeywords.batchInsert(filterKeywordList, ignore = true) {
this[FilterKeywords.id] = it.id
this[FilterKeywords.filterId] = it.filterId
this[FilterKeywords.keyword] = it.keyword
this[FilterKeywords.mode] = it.mode.name
}
}
override suspend fun findById(id: Long): FilterKeyword? = query {
return@query FilterKeywords.selectAll().where { FilterKeywords.id eq id }.singleOrNull()?.toFilterKeyword()
}
override suspend fun deleteById(id: Long): Unit = query {
FilterKeywords.deleteWhere { FilterKeywords.id eq id }
}
override suspend fun deleteByFilterId(filterId: Long): Unit = query {
FilterKeywords.deleteWhere { FilterKeywords.filterId eq filterId }
}
companion object {
private val logger = LoggerFactory.getLogger(ExposedFilterKeywordRepository::class.java)
}
}
fun ResultRow.toFilterKeyword(): FilterKeyword {
return FilterKeyword(
this[FilterKeywords.id],
this[FilterKeywords.filterId],
this[FilterKeywords.keyword],
this[FilterKeywords.mode].let { FilterMode.valueOf(it) }
)
}
object FilterKeywords : Table("filter_keywords") {
val id = long("id")
val filterId = long("filter_id").references(Filters.id)
val keyword = varchar("keyword", 1000)
val mode = varchar("mode", 100)
override val primaryKey: PrimaryKey = PrimaryKey(id)
}

View File

@ -1,104 +0,0 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.application.service.id.IdGenerateService
import dev.usbharu.hideout.core.domain.model.filter.Filter
import dev.usbharu.hideout.core.domain.model.filter.FilterAction
import dev.usbharu.hideout.core.domain.model.filter.FilterRepository
import dev.usbharu.hideout.core.domain.model.filter.FilterType
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
@Repository
class ExposedFilterRepository(private val idGenerateService: IdGenerateService) : FilterRepository,
AbstractRepository() {
override val logger: Logger
get() = Companion.logger
override suspend fun generateId(): Long = idGenerateService.generateId()
override suspend fun save(filter: Filter): Filter = query {
val empty = Filters.selectAll().where {
Filters.id eq filter.id
}.forUpdate().empty()
if (empty) {
Filters.insert {
it[id] = filter.id
it[userId] = filter.userId
it[name] = filter.name
it[context] = filter.context.joinToString(",") { filterType -> filterType.name }
it[filterAction] = filter.filterAction.name
}
} else {
Filters.update({ Filters.id eq filter.id }) {
it[userId] = filter.userId
it[name] = filter.name
it[context] = filter.context.joinToString(",") { filterType -> filterType.name }
it[filterAction] = filter.filterAction.name
}
}
filter
}
override suspend fun findById(id: Long): Filter? = query {
return@query Filters.selectAll().where { Filters.id eq id }.singleOrNull()?.toFilter()
}
override suspend fun findByUserIdAndId(userId: Long, id: Long): Filter? = query {
return@query Filters.selectAll().where { Filters.userId eq userId and (Filters.id eq id) }.singleOrNull()
?.toFilter()
}
override suspend fun findByUserIdAndType(userId: Long, types: List<FilterType>): List<Filter> = query {
return@query Filters.selectAll().where { Filters.userId eq userId }.map { it.toFilter() }
.filter { it.context.containsAll(types) }
}
override suspend fun deleteById(id: Long): Unit = query {
Filters.deleteWhere { Filters.id eq id }
}
override suspend fun deleteByUserIdAndId(userId: Long, id: Long) {
Filters.deleteWhere { Filters.userId eq userId and (Filters.id eq id) }
}
companion object {
private val logger = LoggerFactory.getLogger(ExposedFilterRepository::class.java)
}
}
fun ResultRow.toFilter(): Filter = Filter(
this[Filters.id],
this[Filters.userId],
this[Filters.name],
this[Filters.context].split(",").filterNot(String::isEmpty).map { FilterType.valueOf(it) },
this[Filters.filterAction].let { FilterAction.valueOf(it) }
)
object Filters : Table() {
val id = long("id")
val userId = long("user_id").references(Actors.id)
val name = varchar("name", 255)
val context = varchar("context", 500)
val filterAction = varchar("action", 255)
override val primaryKey: PrimaryKey = PrimaryKey(id)
}

View File

@ -20,34 +20,37 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.post.*
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventPublisher
import dev.usbharu.hideout.core.domain.shared.repository.DomainEventPublishableRepository
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.actorId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.apId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.content
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.createdAt
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.deleted
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.hide
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.id
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.moveTo
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.overview
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.replyId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.repostId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.sensitive
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.text
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.url
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts2.visibility
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.actorId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.apId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.content
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.createdAt
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.deleted
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.hide
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.id
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.moveTo
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.overview
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.replyId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.repostId
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.sensitive
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.text
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.url
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts.visibility
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
import org.jetbrains.exposed.sql.javatime.timestamp
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
@Repository
class ExposedPost2Repository(override val domainEventPublisher: DomainEventPublisher) : Post2Repository,
AbstractRepository(), DomainEventPublishableRepository<Post2> {
override suspend fun save(post: Post2): Post2 {
class ExposedPostRepository(override val domainEventPublisher: DomainEventPublisher) :
PostRepository,
AbstractRepository(),
DomainEventPublishableRepository<Post> {
override suspend fun save(post: Post): Post {
query {
Posts2.upsert {
Posts.upsert {
it[id] = post.id.id
it[actorId] = post.actorId.id
it[overview] = post.overview?.overview
@ -70,6 +73,9 @@ class ExposedPost2Repository(override val domainEventPublisher: DomainEventPubli
PostsEmojis.deleteWhere {
postId eq post.id.id
}
PostsVisibleActors.deleteWhere {
postId eq post.id.id
}
PostsMedia.batchInsert(post.mediaIds) {
this[PostsMedia.postId] = post.id.id
this[PostsMedia.mediaId] = it.id
@ -78,14 +84,18 @@ class ExposedPost2Repository(override val domainEventPublisher: DomainEventPubli
this[PostsEmojis.postId] = post.id.id
this[PostsEmojis.emojiId] = it.emojiId
}
PostsVisibleActors.batchInsert(post.visibleActors) {
this[PostsVisibleActors.postId] = post.id.id
this[PostsVisibleActors.actorId] = it.id
}
}
update(post)
return post
}
override suspend fun saveAll(posts: List<Post2>): List<Post2> {
override suspend fun saveAll(posts: List<Post>): List<Post> {
query {
Posts2.batchUpsert(posts, id) {
Posts.batchUpsert(posts, id) {
this[id] = it.id.id
this[actorId] = it.actorId.id
this[overview] = it.overview?.overview
@ -103,15 +113,30 @@ class ExposedPost2Repository(override val domainEventPublisher: DomainEventPubli
this[moveTo] = it.moveTo?.id
}
val mediaIds = posts.flatMap { post -> post.mediaIds.map { post.id.id to it.id } }
PostsMedia.batchUpsert(mediaIds, PostsMedia.postId) {
val postsIds = posts.map { it.id.id }
PostsMedia.deleteWhere {
postId inList postsIds
}
PostsMedia.batchInsert(mediaIds) {
this[PostsMedia.postId] = it.first
this[PostsMedia.mediaId] = it.second
}
val emojiIds = posts.flatMap { post -> post.emojiIds.map { post.id.id to it.emojiId } }
PostsEmojis.batchUpsert(emojiIds, PostsEmojis.postId) {
PostsEmojis.deleteWhere {
postId inList postsIds
}
PostsEmojis.batchInsert(emojiIds) {
this[PostsEmojis.postId] = it.first
this[PostsEmojis.emojiId] = it.second
}
val visibleActors = posts.flatMap { post -> post.visibleActors.map { post.id.id to it.id } }
PostsVisibleActors.deleteWhere {
postId inList postsIds
}
PostsVisibleActors.batchInsert(visibleActors) {
this[PostsVisibleActors.postId] = it.first
this[PostsVisibleActors.actorId] = it.second
}
}
posts.forEach {
update(it)
@ -119,17 +144,17 @@ class ExposedPost2Repository(override val domainEventPublisher: DomainEventPubli
return posts
}
override suspend fun findById(id: PostId): Post2? {
override suspend fun findById(id: PostId): Post? {
TODO("Not yet implemented")
}
override suspend fun findByActorId(id: ActorId): List<Post2> {
override suspend fun findByActorId(id: ActorId): List<Post> {
TODO("Not yet implemented")
}
override suspend fun delete(post: Post2) {
override suspend fun delete(post: Post) {
query {
Posts2.deleteWhere {
Posts.deleteWhere {
id eq post.id.id
}
}
@ -139,13 +164,13 @@ class ExposedPost2Repository(override val domainEventPublisher: DomainEventPubli
override val logger: Logger = Companion.logger
companion object {
private val logger = LoggerFactory.getLogger(ExposedPost2Repository::class.java)
private val logger = LoggerFactory.getLogger(ExposedPostRepository::class.java)
}
}
object Posts2 : Table("posts") {
object Posts : Table("posts") {
val id = long("id")
val actorId = long("actor_id").references(Actors2.id)
val actorId = long("actor_id").references(Actors.id)
val overview = varchar("overview", PostOverview.length).nullable()
val content = varchar("content", PostContent.contentLength)
val text = varchar("text", PostContent.textLength)
@ -159,7 +184,6 @@ object Posts2 : Table("posts") {
val deleted = bool("deleted")
val hide = bool("hide")
val moveTo = long("move_to").references(id).nullable()
}
object PostsMedia : Table("posts_media") {
@ -172,4 +196,9 @@ object PostsEmojis : Table("posts_emojis") {
val postId = long("post_id").references(id)
val emojiId = long("emoji_id").references(CustomEmojis.id)
override val primaryKey: PrimaryKey = PrimaryKey(postId, emojiId)
}
}
object PostsVisibleActors : Table("posts_visible_actors") {
val postId = long("post_id").references(id)
val actorId = long("actor_id").references(Actors.id)
}

View File

@ -1,150 +0,0 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.application.service.id.IdGenerateService
import dev.usbharu.hideout.core.domain.model.post.Visibility
import org.jetbrains.exposed.sql.*
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Repository
@Repository
@Qualifier("jdbc")
@ConditionalOnProperty("hideout.use-mongodb", havingValue = "false", matchIfMissing = true)
class ExposedTimelineRepository(private val idGenerateService: IdGenerateService) : TimelineRepository,
AbstractRepository() {
override val logger: Logger
get() = Companion.logger
override suspend fun generateId(): Long = idGenerateService.generateId()
override suspend fun save(timeline: Timeline): Timeline = query {
if (Timelines.selectAll().where { Timelines.id eq timeline.id }.forUpdate().singleOrNull() == null) {
Timelines.insert {
it[id] = timeline.id
it[userId] = timeline.userId
it[timelineId] = timeline.timelineId
it[postId] = timeline.postId
it[postActorId] = timeline.postActorId
it[createdAt] = timeline.createdAt
it[replyId] = timeline.replyId
it[repostId] = timeline.repostId
it[visibility] = timeline.visibility.ordinal
it[sensitive] = timeline.sensitive
it[isLocal] = timeline.isLocal
it[isPureRepost] = timeline.isPureRepost
it[mediaIds] = timeline.mediaIds.joinToString(",")
it[emojiIds] = timeline.emojiIds.joinToString(",")
}
} else {
Timelines.update({ Timelines.id eq timeline.id }) {
it[userId] = timeline.userId
it[timelineId] = timeline.timelineId
it[postId] = timeline.postId
it[postActorId] = timeline.postActorId
it[createdAt] = timeline.createdAt
it[replyId] = timeline.replyId
it[repostId] = timeline.repostId
it[visibility] = timeline.visibility.ordinal
it[sensitive] = timeline.sensitive
it[isLocal] = timeline.isLocal
it[isPureRepost] = timeline.isPureRepost
it[mediaIds] = timeline.mediaIds.joinToString(",")
it[emojiIds] = timeline.emojiIds.joinToString(",")
}
}
return@query timeline
}
override suspend fun saveAll(timelines: List<Timeline>): List<Timeline> = query {
Timelines.batchInsert(timelines, true, false) {
this[Timelines.id] = it.id
this[Timelines.userId] = it.userId
this[Timelines.timelineId] = it.timelineId
this[Timelines.postId] = it.postId
this[Timelines.postActorId] = it.postActorId
this[Timelines.createdAt] = it.createdAt
this[Timelines.replyId] = it.replyId
this[Timelines.repostId] = it.repostId
this[Timelines.visibility] = it.visibility.ordinal
this[Timelines.sensitive] = it.sensitive
this[Timelines.isLocal] = it.isLocal
this[Timelines.isPureRepost] = it.isPureRepost
this[Timelines.mediaIds] = it.mediaIds.joinToString(",")
this[Timelines.emojiIds] = it.emojiIds.joinToString(",")
}
return@query timelines
}
override suspend fun findByUserId(id: Long): List<Timeline> = query {
return@query Timelines.selectAll().where { Timelines.userId eq id }.map { it.toTimeline() }
}
override suspend fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline> = query {
return@query Timelines.selectAll().where { Timelines.userId eq userId and (Timelines.timelineId eq timelineId) }
.map { it.toTimeline() }
}
companion object {
private val logger = LoggerFactory.getLogger(ExposedTimelineRepository::class.java)
}
}
fun ResultRow.toTimeline(): Timeline {
return Timeline(
id = this[Timelines.id],
userId = this[Timelines.userId],
timelineId = this[Timelines.timelineId],
postId = this[Timelines.postId],
postActorId = this[Timelines.postActorId],
createdAt = this[Timelines.createdAt],
replyId = this[Timelines.replyId],
repostId = this[Timelines.repostId],
visibility = Visibility.values().first { it.ordinal == this[Timelines.visibility] },
sensitive = this[Timelines.sensitive],
isLocal = this[Timelines.isLocal],
isPureRepost = this[Timelines.isPureRepost],
mediaIds = this[Timelines.mediaIds].split(",").mapNotNull { it.toLongOrNull() },
emojiIds = this[Timelines.emojiIds].split(",").mapNotNull { it.toLongOrNull() }
)
}
object Timelines : Table("timelines") {
val id = long("id")
val userId = long("user_id")
val timelineId = long("timeline_id")
val postId = long("post_id")
val postActorId = long("post_actor_id")
val createdAt = long("created_at")
val replyId = long("reply_id").nullable()
val repostId = long("repost_id").nullable()
val visibility = integer("visibility")
val sensitive = bool("sensitive")
val isLocal = bool("is_local")
val isPureRepost = bool("is_pure_repost")
val mediaIds = varchar("media_ids", 255)
val emojiIds = varchar("emoji_ids", 255)
override val primaryKey: PrimaryKey = PrimaryKey(id)
init {
uniqueIndex(userId, timelineId, postId)
}
}

View File

@ -16,69 +16,67 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.application.service.id.IdGenerateService
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
import dev.usbharu.hideout.core.domain.model.instance.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.timestamp
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
import java.net.URI
import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity
@Repository
class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) : InstanceRepository,
class InstanceRepositoryImpl : InstanceRepository,
AbstractRepository() {
override val logger: Logger
get() = Companion.logger
override suspend fun generateId(): Long = idGenerateService.generateId()
override suspend fun save(instance: InstanceEntity): InstanceEntity = query {
if (Instance.selectAll().where { Instance.id.eq(instance.id) }.forUpdate().empty()) {
if (Instance.selectAll().where { Instance.id.eq(instance.id.instanceId) }.forUpdate().empty()) {
Instance.insert {
it[id] = instance.id
it[name] = instance.name
it[description] = instance.description
it[url] = instance.url
it[iconUrl] = instance.iconUrl
it[sharedInbox] = instance.sharedInbox
it[software] = instance.software
it[version] = instance.version
it[id] = instance.id.instanceId
it[name] = instance.name.name
it[description] = instance.description.description
it[url] = instance.url.toString()
it[iconUrl] = instance.iconUrl.toString()
it[sharedInbox] = instance.sharedInbox?.toString()
it[software] = instance.software.software
it[version] = instance.version.version
it[isBlocked] = instance.isBlocked
it[isMuted] = instance.isMuted
it[moderationNote] = instance.moderationNote
it[moderationNote] = instance.moderationNote.note
it[createdAt] = instance.createdAt
}
} else {
Instance.update({ Instance.id eq instance.id }) {
it[name] = instance.name
it[description] = instance.description
it[url] = instance.url
it[iconUrl] = instance.iconUrl
it[sharedInbox] = instance.sharedInbox
it[software] = instance.software
it[version] = instance.version
Instance.update({ Instance.id eq instance.id.instanceId }) {
it[name] = instance.name.name
it[description] = instance.description.description
it[url] = instance.url.toString()
it[iconUrl] = instance.iconUrl.toString()
it[sharedInbox] = instance.sharedInbox?.toString()
it[software] = instance.software.software
it[version] = instance.version.version
it[isBlocked] = instance.isBlocked
it[isMuted] = instance.isMuted
it[moderationNote] = instance.moderationNote
it[moderationNote] = instance.moderationNote.note
it[createdAt] = instance.createdAt
}
}
return@query instance
}
override suspend fun findById(id: Long): InstanceEntity? = query {
return@query Instance.selectAll().where { Instance.id eq id }
override suspend fun findById(id: InstanceId): InstanceEntity? = query {
return@query Instance.selectAll().where { Instance.id eq id.instanceId }
.singleOrNull()?.toInstance()
}
override suspend fun delete(instance: InstanceEntity): Unit = query {
Instance.deleteWhere { id eq instance.id }
Instance.deleteWhere { id eq instance.id.instanceId }
}
override suspend fun findByUrl(url: String): dev.usbharu.hideout.core.domain.model.instance.Instance? = query {
return@query Instance.selectAll().where { Instance.url eq url }.singleOrNull()?.toInstance()
override suspend fun findByUrl(url: URI): dev.usbharu.hideout.core.domain.model.instance.Instance? = query {
return@query Instance.selectAll().where { Instance.url eq url.toString() }.singleOrNull()?.toInstance()
}
companion object {
@ -88,17 +86,17 @@ class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) :
fun ResultRow.toInstance(): InstanceEntity {
return InstanceEntity(
id = this[Instance.id],
name = this[Instance.name],
description = this[Instance.description],
url = this[Instance.url],
iconUrl = this[Instance.iconUrl],
sharedInbox = this[Instance.sharedInbox],
software = this[Instance.software],
version = this[Instance.version],
id = InstanceId(this[Instance.id]),
name = InstanceName(this[Instance.name]),
description = InstanceDescription(this[Instance.description]),
url = URI.create(this[Instance.url]),
iconUrl = URI.create(this[Instance.iconUrl]),
sharedInbox = this[Instance.sharedInbox]?.let { URI.create(it) },
software = InstanceSoftware(this[Instance.software]),
version = InstanceVersion(this[Instance.version]),
isBlocked = this[Instance.isBlocked],
isMuted = this[Instance.isMuted],
moderationNote = this[Instance.moderationNote],
moderationNote = InstanceModerationNote(this[Instance.moderationNote]),
createdAt = this[Instance.createdAt]
)
}

View File

@ -16,105 +16,82 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.application.service.id.IdGenerateService
import dev.usbharu.hideout.core.domain.model.media.FileType
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
import dev.usbharu.hideout.core.domain.model.media.MimeType
import dev.usbharu.hideout.core.domain.model.media.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
import java.net.URI
import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia
@Repository
class MediaRepositoryImpl(private val idGenerateService: IdGenerateService) : MediaRepository, AbstractRepository() {
class MediaRepositoryImpl : MediaRepository, AbstractRepository() {
override suspend fun findById(id: MediaId): dev.usbharu.hideout.core.domain.model.media.Media? {
return query {
return@query Media
.selectAll().where { Media.id eq id.id }
.singleOrNull()
?.toMedia()
}
}
override suspend fun delete(media: dev.usbharu.hideout.core.domain.model.media.Media): Unit = query {
Media.deleteWhere {
id eq id
}
}
override val logger: Logger
get() = Companion.logger
override suspend fun generateId(): Long = idGenerateService.generateId()
override suspend fun save(media: EntityMedia): EntityMedia = query {
if (Media.selectAll().where { Media.id eq media.id }.forUpdate().singleOrNull() != null
if (Media.selectAll().where { Media.id eq media.id.id }.forUpdate().singleOrNull() != null
) {
Media.update({ Media.id eq media.id }) {
it[name] = media.name
it[url] = media.url
it[remoteUrl] = media.remoteUrl
it[thumbnailUrl] = media.thumbnailUrl
it[type] = media.type.ordinal
it[blurhash] = media.blurHash
Media.update({ Media.id eq media.id.id }) {
it[name] = media.name.name
it[url] = media.url.toString()
it[remoteUrl] = media.remoteUrl?.toString()
it[thumbnailUrl] = media.thumbnailUrl?.toString()
it[type] = media.type.name
it[blurhash] = media.blurHash?.hash
it[mimeType] = media.mimeType.type + "/" + media.mimeType.subtype
it[description] = media.description
it[description] = media.description?.description
}
} else {
Media.insert {
it[id] = media.id
it[name] = media.name
it[url] = media.url
it[remoteUrl] = media.remoteUrl
it[thumbnailUrl] = media.thumbnailUrl
it[type] = media.type.ordinal
it[blurhash] = media.blurHash
it[id] = media.id.id
it[name] = media.name.name
it[url] = media.url.toString()
it[remoteUrl] = media.remoteUrl?.toString()
it[thumbnailUrl] = media.thumbnailUrl?.toString()
it[type] = media.type.name
it[blurhash] = media.blurHash?.hash
it[mimeType] = media.mimeType.type + "/" + media.mimeType.subtype
it[description] = media.description
it[description] = media.description?.description
}
}
return@query media
}
override suspend fun findById(id: Long): EntityMedia? = query {
return@query Media
.selectAll().where { Media.id eq id }
.singleOrNull()
?.toMedia()
}
override suspend fun delete(id: Long): Unit = query {
Media.deleteWhere {
Media.id eq id
}
}
override suspend fun findByRemoteUrl(remoteUrl: String): dev.usbharu.hideout.core.domain.model.media.Media? =
query {
return@query Media.selectAll().where { Media.remoteUrl eq remoteUrl }.singleOrNull()?.toMedia()
}
companion object {
private val logger = LoggerFactory.getLogger(MediaRepositoryImpl::class.java)
}
}
fun ResultRow.toMedia(): EntityMedia {
val fileType = FileType.values().first { it.ordinal == this[Media.type] }
val fileType = FileType.valueOf(this[Media.type])
val mimeType = this[Media.mimeType]
return EntityMedia(
id = this[Media.id],
name = this[Media.name],
url = this[Media.url],
remoteUrl = this[Media.remoteUrl],
thumbnailUrl = this[Media.thumbnailUrl],
id = MediaId(this[Media.id]),
name = MediaName(this[Media.name]),
url = URI.create(this[Media.url]),
remoteUrl = this[Media.remoteUrl]?.let { URI.create(it) },
thumbnailUrl = this[Media.thumbnailUrl]?.let { URI.create(it) },
type = fileType,
blurHash = this[Media.blurhash],
blurHash = this[Media.blurhash]?.let { MediaBlurHash(it) },
mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType),
description = this[Media.description]
)
}
fun ResultRow.toMediaOrNull(): EntityMedia? {
val fileType = FileType.values().first { it.ordinal == (this.getOrNull(Media.type) ?: return null) }
val mimeType = this.getOrNull(Media.mimeType) ?: return null
return EntityMedia(
id = this.getOrNull(Media.id) ?: return null,
name = this.getOrNull(Media.name) ?: return null,
url = this.getOrNull(Media.url) ?: return null,
remoteUrl = this[Media.remoteUrl],
thumbnailUrl = this[Media.thumbnailUrl],
type = FileType.values().first { it.ordinal == this.getOrNull(Media.type) },
blurHash = this[Media.blurhash],
mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType),
description = this[Media.description]
description = this[Media.description]?.let { MediaDescription(it) }
)
}
@ -124,7 +101,7 @@ object Media : Table("media") {
val url = varchar("url", 255).uniqueIndex()
val remoteUrl = varchar("remote_url", 255).uniqueIndex().nullable()
val thumbnailUrl = varchar("thumbnail_url", 255).uniqueIndex().nullable()
val type = integer("type")
val type = varchar("type", 100)
val blurhash = varchar("blurhash", 255).nullable()
val mimeType = varchar("mime_type", 255)
val description = varchar("description", 4000).nullable()

View File

@ -1,62 +0,0 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import org.jetbrains.exposed.sql.*
import org.springframework.stereotype.Repository
import java.util.*
@Repository
class MetaRepositoryImpl : MetaRepository {
override suspend fun save(meta: dev.usbharu.hideout.core.domain.model.meta.Meta) {
if (Meta.selectAll().where { Meta.id eq 1 }.empty()) {
Meta.insert {
it[id] = 1
it[version] = meta.version
it[kid] = UUID.randomUUID().toString()
it[jwtPrivateKey] = meta.jwt.privateKey
it[jwtPublicKey] = meta.jwt.publicKey
}
} else {
Meta.update({ Meta.id eq 1 }) {
it[version] = meta.version
it[kid] = UUID.randomUUID().toString()
it[jwtPrivateKey] = meta.jwt.privateKey
it[jwtPublicKey] = meta.jwt.publicKey
}
}
}
override suspend fun get(): dev.usbharu.hideout.core.domain.model.meta.Meta? {
return Meta.selectAll().where { Meta.id eq 1 }.singleOrNull()?.let {
dev.usbharu.hideout.core.domain.model.meta.Meta(
it[Meta.version],
Jwt(UUID.fromString(it[Meta.kid]), it[Meta.jwtPrivateKey], it[Meta.jwtPublicKey])
)
}
}
}
object Meta : Table("meta_info") {
val id: Column<Long> = long("id")
val version: Column<String> = varchar("version", 1000)
val kid: Column<String> = varchar("kid", 1000)
val jwtPrivateKey: Column<String> = varchar("jwt_private_key", 100000)
val jwtPublicKey: Column<String> = varchar("jwt_public_key", 100000)
override val primaryKey: PrimaryKey = PrimaryKey(id)
}

View File

@ -16,14 +16,14 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import org.jetbrains.exposed.dao.id.LongIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.update
import org.jetbrains.exposed.sql.javatime.timestamp
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Repository
@ -35,24 +35,28 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() {
override suspend fun save(userDetail: UserDetail): UserDetail = query {
val singleOrNull =
UserDetails.selectAll().where { UserDetails.actorId eq userDetail.actorId }.forUpdate().singleOrNull()
UserDetails.selectAll().where { UserDetails.id eq userDetail.id.id }.forUpdate().singleOrNull()
if (singleOrNull == null) {
UserDetails.insert {
it[actorId] = userDetail.actorId
it[password] = userDetail.password
it[id] = userDetail.id.id
it[actorId] = userDetail.actorId.id
it[password] = userDetail.password.password
it[autoAcceptFolloweeFollowRequest] = userDetail.autoAcceptFolloweeFollowRequest
it[lastMigration] = userDetail.lastMigration
}
} else {
UserDetails.update({ UserDetails.actorId eq userDetail.actorId }) {
it[password] = userDetail.password
UserDetails.update({ UserDetails.id eq userDetail.id.id }) {
it[actorId] = userDetail.actorId.id
it[password] = userDetail.password.password
it[autoAcceptFolloweeFollowRequest] = userDetail.autoAcceptFolloweeFollowRequest
it[lastMigration] = userDetail.lastMigration
}
}
return@query userDetail
}
override suspend fun delete(userDetail: UserDetail): Unit = query {
UserDetails.deleteWhere { actorId eq userDetail.actorId }
UserDetails.deleteWhere { id eq userDetail.id.id }
}
override suspend fun findByActorId(actorId: Long): UserDetail? = query {
@ -60,10 +64,12 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() {
.selectAll().where { UserDetails.actorId eq actorId }
.singleOrNull()
?.let {
UserDetail(
it[UserDetails.actorId],
it[UserDetails.password],
it[UserDetails.autoAcceptFolloweeFollowRequest]
UserDetail.create(
UserDetailId(it[UserDetails.id]),
ActorId(it[UserDetails.actorId]),
UserDetailHashedPassword(it[UserDetails.password]),
it[UserDetails.autoAcceptFolloweeFollowRequest],
it[UserDetails.lastMigration]
)
}
}
@ -73,8 +79,11 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() {
}
}
object UserDetails : LongIdTable("user_details") {
object UserDetails : Table("user_details") {
val id = long("id")
val actorId = long("actor_id").references(Actors.id)
val password = varchar("password", 255)
val autoAcceptFolloweeFollowRequest = bool("auto_accept_followee_follow_request")
val lastMigration = timestamp("last_migration").nullable()
override val primaryKey: PrimaryKey = PrimaryKey(id)
}

View File

@ -1,41 +0,0 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.factory
import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.model.actor.ActorDescription
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import org.springframework.stereotype.Component
@Component
class ActorDescriptionFactoryImpl(
private val applicationConfig: ApplicationConfig,
private val emojiRepository: CustomEmojiRepository,
) : ActorDescription.ActorDescriptionFactory() {
val regex = Regex(":(w+):")
suspend fun create(description: String): ActorDescription {
val findAll = regex.findAll(description)
val emojis =
emojiRepository.findByNamesAndDomain(
findAll.map { it.groupValues[1] }.toList(),
applicationConfig.url.host
)
return create(description, emojis.map { EmojiId(it.id) })
}
}

View File

@ -26,25 +26,23 @@ import java.net.URI
import java.time.Instant
@Component
class Actor2FactoryImpl(
class ActorFactoryImpl(
private val idGenerateService: IdGenerateService,
private val actorScreenNameFactory: ActorScreenNameFactoryImpl,
private val actorDescriptionFactory: ActorDescriptionFactoryImpl,
private val applicationConfig: ApplicationConfig,
) : Actor2.Actor2Factory() {
) {
suspend fun createLocal(
name: String,
keyPair: Pair<ActorPublicKey, ActorPrivateKey>,
instanceId: InstanceId,
): Actor2 {
): Actor {
val actorName = ActorName(name)
val userUrl = "${applicationConfig.url}/users/${actorName.name}"
return super.internalCreate(
return Actor(
id = ActorId(idGenerateService.generateId()),
name = actorName,
domain = Domain(applicationConfig.url.host),
screenName = actorScreenNameFactory.create(name),
description = actorDescriptionFactory.create(""),
screenName = ActorScreenName(name),
description = ActorDescription(""),
inbox = URI.create("$userUrl/inbox"),
outbox = URI.create("$userUrl/outbox"),
url = applicationConfig.url.toURI(),
@ -59,8 +57,11 @@ class Actor2FactoryImpl(
followersCount = ActorRelationshipCount(0),
followingCount = ActorRelationshipCount(0),
postsCount = ActorPostsCount(0),
lastPostDate = null,
suspend = false
lastPostAt = null,
suspend = false,
emojiIds = emptySet()
)
}
}
}
// todo なんか色々おかしいので直す

View File

@ -1,45 +0,0 @@
/*
* Copyright (C) 2024 usbharu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.usbharu.hideout.core.infrastructure.factory
import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.model.actor.ActorScreenName
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import org.springframework.stereotype.Component
@Component
class ActorScreenNameFactoryImpl(
private val applicationConfig: ApplicationConfig,
private val emojiRepository: CustomEmojiRepository,
) : ActorScreenName.ActorScreenNameFactory() {
val regex = Regex(":(w+):")
suspend fun create(content: String): ActorScreenName {
val findAll = regex.findAll(content)
val emojis =
emojiRepository.findByNamesAndDomain(
findAll.map { it.groupValues[1] }.toList(),
applicationConfig.url.host
)
return create(content, emojis.map { EmojiId(it.id) })
}
}

View File

@ -32,4 +32,4 @@ class PostContentFactoryImpl(
emptyList()
)
}
}
}

View File

@ -21,7 +21,7 @@ import dev.usbharu.hideout.application.service.id.IdGenerateService
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorName
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.Post2
import dev.usbharu.hideout.core.domain.model.post.Post
import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.model.post.PostOverview
import dev.usbharu.hideout.core.domain.model.post.Visibility
@ -34,7 +34,7 @@ class PostFactoryImpl(
private val idGenerateService: IdGenerateService,
private val postContentFactoryImpl: PostContentFactoryImpl,
private val applicationConfig: ApplicationConfig,
) : Post2.PostFactory() {
) : Post.PostFactory() {
suspend fun createLocal(
actorId: ActorId,
actorName: ActorName,
@ -45,7 +45,7 @@ class PostFactoryImpl(
replyId: PostId?,
sensitive: Boolean,
mediaIds: List<MediaId>,
): Post2 {
): Post {
val id = idGenerateService.generateId()
val url = URI.create(applicationConfig.url.toString() + "/users/" + actorName + "/posts/" + id)
return super.create(
@ -64,4 +64,4 @@ class PostFactoryImpl(
mediaIds
)
}
}
}

View File

@ -27,4 +27,4 @@ class SpringFrameworkDomainEventPublisher(private val applicationEventPublisher:
override suspend fun publishEvent(domainEvent: DomainEvent) {
applicationEventPublisher.publishEvent(domainEvent)
}
}
}

View File

@ -16,32 +16,16 @@
package dev.usbharu.hideout.core.interfaces.api.auth
import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.application.config.CaptchaConfig
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.PostMapping
@Controller
class AuthController(
private val authApiService: AuthApiService,
private val captchaConfig: CaptchaConfig,
private val applicationConfig: ApplicationConfig
) {
interface AuthController {
@GetMapping("/auth/sign_up")
fun signUp(model: Model): String {
model.addAttribute("siteKey", captchaConfig.reCaptchaSiteKey)
model.addAttribute("applicationConfig", applicationConfig)
return "sign_up"
}
fun signUp(model: Model): String
@PostMapping("/auth/sign_up")
suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm): String {
return "redirect:" + registerAccount.url
}
suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm): String
}

View File

@ -16,43 +16,21 @@
package dev.usbharu.hideout.core.interfaces.api.media
import dev.usbharu.hideout.application.config.LocalStorageConfig
import dev.usbharu.hideout.core.service.media.FileTypeDeterminationService
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.PathResource
import org.springframework.core.io.Resource
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import java.nio.file.Path
import kotlin.io.path.name
@Controller
@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true)
class LocalFileController(
localStorageConfig: LocalStorageConfig,
private val fileTypeDeterminationService: FileTypeDeterminationService
) {
private val savePath = Path.of(localStorageConfig.path).toAbsolutePath()
interface LocalFileController {
@GetMapping("/files/{id}")
fun files(@PathVariable("id") id: String): ResponseEntity<Resource> {
val name = Path.of(id).name
val path = savePath.resolve(name)
val mimeType = fileTypeDeterminationService.fileType(path, name)
val pathResource = PathResource(path)
return ResponseEntity
.ok()
.contentType(MediaType(mimeType.type, mimeType.subtype))
.contentLength(pathResource.contentLength())
.body(pathResource)
}
fun files(@PathVariable("id") id: String): ResponseEntity<Resource>
@GetMapping("/users/{user}/icon.jpg", "/users/{user}/header.jpg")
fun icons(): ResponseEntity<Resource> {

View File

@ -44,5 +44,4 @@ object RsaUtil {
}
fun decodeRsaPrivateKey(encoded: String): RSAPrivateKey = decodeRsaPrivateKey(Base64Util.decode(encoded))
}

View File

@ -3,7 +3,7 @@ package dev.usbharu.hideout.core.domain.model.actor
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
class Actors2Test {
class ActorsTest {
@Test
fun alsoKnownAsに自分自身が含まれてはいけない() {
val actor = TestActor2Factory.create(publicKey = ActorPublicKey(""))

View File

@ -7,7 +7,7 @@ import kotlinx.coroutines.runBlocking
import java.net.URI
import java.time.Instant
object TestActor2Factory : Actor2.Actor2Factory() {
object TestActor2Factory : Actor.Actor2Factory() {
private val idGenerateService = TwitterSnowflakeIdGenerateService
fun create(
@ -31,8 +31,8 @@ object TestActor2Factory : Actor2.Actor2Factory() {
followingCount: Int = 0,
postCount: Int = 0,
lastPostDate: Instant? = null,
suspend: Boolean = false
): Actor2 {
suspend: Boolean = false,
): Actor {
return runBlocking {
super.internalCreate(
id = ActorId(id),