feat: /users/:idを追加

This commit is contained in:
usbharu 2024-08-27 10:43:08 +09:00
parent c48694ab0b
commit 992cc18c62
Signed by: usbharu
GPG Key ID: 8CB1087135660B8D
9 changed files with 155 additions and 2 deletions

View File

@ -0,0 +1,38 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.domain.model.actor.Actor
import java.net.URI
data class ActorDetail(
val id: Long,
val name: String,
val screenName: String,
val host: String,
val remoteUrl: String?,
val locked: Boolean,
val description: String,
val postsCount: Int,
val iconUrl: URI?,
val bannerURL: URI?,
val followingCount: Int?,
val followersCount: Int?
) {
companion object {
fun of(actor: Actor, iconUrl: URI?, bannerURL: URI?): ActorDetail {
return ActorDetail(
id = actor.id.id,
name = actor.name.name,
screenName = actor.screenName.screenName,
host = actor.url.host,
remoteUrl = actor.url.toString(),
locked = actor.locked,
description = actor.description.description,
postsCount = actor.postsCount.postsCount,
iconUrl = iconUrl,
bannerURL = bannerURL,
followingCount = actor.followingCount?.relationshipCount,
followersCount = actor.followersCount?.relationshipCount
)
}
}
}

View File

@ -0,0 +1,8 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
data class GetActorDetail(
val actorName: Acct? = null,
val id: Long? = null
)

View File

@ -0,0 +1,49 @@
package dev.usbharu.hideout.core.application.actor
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
import dev.usbharu.hideout.core.application.shared.Transaction
import dev.usbharu.hideout.core.config.ApplicationConfig
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.MediaRepository
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
@Component
class GetActorDetailApplicationService(
private val actorRepository: ActorRepository,
private val mediaRepository: MediaRepository,
private val applicationConfig: ApplicationConfig,
transaction: Transaction
) :
AbstractApplicationService<GetActorDetail, ActorDetail>(
transaction, logger
) {
override suspend fun internalExecute(command: GetActorDetail, principal: Principal): ActorDetail {
val actor = if (command.id != null) {
actorRepository.findById(ActorId(command.id))
?: throw IllegalArgumentException("Actor ${command.id} not found.")
} else if (command.actorName != null) {
val host = if (command.actorName.host.isEmpty()) {
applicationConfig.url.host
} else {
command.actorName.host
}
actorRepository.findByNameAndDomain(command.actorName.userpart, host)
?: throw IllegalArgumentException("Actor ${command.actorName} not found.")
} else {
throw IllegalArgumentException("id and actorName are null.")
}
val iconUrl = actor.icon?.let { mediaRepository.findById(it)?.url }
val bannerUrl = actor.banner?.let { mediaRepository.findById(it)?.url }
return ActorDetail.of(actor, iconUrl, bannerUrl)
}
companion object {
private val logger = LoggerFactory.getLogger(GetActorDetailApplicationService::class.java)
}
}

View File

@ -87,6 +87,7 @@ class SecurityConfig {
authorize(GET, "/auth/sign_up", hasRole("ANONYMOUS"))
authorize(POST, "/auth/sign_up", permitAll)
authorize(GET, "/users/{username}/posts/{postId}", permitAll)
authorize(GET, "/users/{userid}", permitAll)
authorize(GET, "/files/*", permitAll)
authorize(POST, "/publish", authenticated)
authorize(GET, "/publish", authenticated)

View File

@ -5,4 +5,14 @@ data class Acct(
val host: String
) {
override fun toString(): String = "acct:$userpart@$host"
companion object {
fun of(acct: String): Acct {
return Acct(
acct.substringBeforeLast('@'),
acct.substringAfterLast('@', "")
)
}
}
}

View File

@ -0,0 +1,31 @@
package dev.usbharu.hideout.core.interfaces.web.user
import dev.usbharu.hideout.core.application.actor.GetActorDetail
import dev.usbharu.hideout.core.application.actor.GetActorDetailApplicationService
import dev.usbharu.hideout.core.application.instance.GetLocalInstanceApplicationService
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityFormLoginPrincipalContextHolder
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
@Controller
class UserController(
private val getLocalInstanceApplicationService: GetLocalInstanceApplicationService,
private val getUserDetailApplicationService: GetActorDetailApplicationService,
private val springSecurityFormLoginPrincipalContextHolder: SpringSecurityFormLoginPrincipalContextHolder,
) {
@GetMapping("/users/{name}")
suspend fun userById(@PathVariable name: String, model: Model): String {
val principal = springSecurityFormLoginPrincipalContextHolder.getPrincipal()
model.addAttribute("instance", getLocalInstanceApplicationService.execute(Unit, Anonymous))
model.addAttribute(
"user",
getUserDetailApplicationService.execute(GetActorDetail(Acct.of(name)), principal)
)
return "userById"
}
}

View File

@ -22,3 +22,4 @@ post-form.new-posts-form-label=\u4ECA\u306A\u306B\u3057\u3066\u308B?
post-form.new-posts-submit=\u6295\u7A3F\u3059\u308B
post.repost=\u30EA\u30DD\u30B9\u30C8
post.repost-by={0}\u304C\u30EA\u30DD\u30B9\u30C8
user-by-id.title={0} \u3055\u3093 - {1}

View File

@ -22,3 +22,4 @@ post-form.new-posts-form-label=\u4ECA\u306A\u306B\u3057\u3066\u308B?
post-form.new-posts-submit=\u6295\u7A3F\u3059\u308B
post.repost=\u30EA\u30DD\u30B9\u30C8
post.repost-by={0}\u304C\u30EA\u30DD\u30B9\u30C8
user-by-id.title={0} \u3055\u3093 - {1}

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="#{user-by-id.title(${user.screenName},${instance.name})}">User - hideout</title>
</head>
<body>
<noscript>
<div>
<img th:src="user.icon">
</div>
</noscript>
</body>
</html>