feat: 投稿のap idにアクセスすると投稿を取得できるように

This commit is contained in:
usbharu 2023-10-21 00:31:54 +09:00
parent 108f521f23
commit 1547c0edaa
9 changed files with 161 additions and 2 deletions

View File

@ -0,0 +1,16 @@
package dev.usbharu.hideout.controller
import dev.usbharu.hideout.domain.model.ap.Note
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.CurrentSecurityContext
import org.springframework.security.core.context.SecurityContext
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
interface NoteApController {
@GetMapping("/users/*/posts/{postId}")
suspend fun postsAp(
@PathVariable("postId") postId: Long,
@CurrentSecurityContext context: SecurityContext
): ResponseEntity<Note>
}

View File

@ -0,0 +1,32 @@
package dev.usbharu.hideout.controller
import dev.usbharu.hideout.domain.model.ap.Note
import dev.usbharu.hideout.service.api.NoteApApiService
import dev.usbharu.hideout.service.signature.HttpSignatureUser
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.CurrentSecurityContext
import org.springframework.security.core.context.SecurityContext
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
@RestController
class NoteApControllerImpl(private val noteApApiService: NoteApApiService) : NoteApController {
override suspend fun postsAp(
@PathVariable(value = "postId") postId: Long, @CurrentSecurityContext context: SecurityContext
): ResponseEntity<Note> {
val userId =
if (context.authentication is PreAuthenticatedAuthenticationToken && context.authentication.details is HttpSignatureUser) {
(context.authentication.details as HttpSignatureUser).id
} else {
null
}
val note = noteApApiService.getNote(postId, userId)
if (note != null) {
return ResponseEntity.ok(note)
}
return ResponseEntity.notFound().build()
}
}

View File

@ -1,9 +1,7 @@
package dev.usbharu.hideout.query
import dev.usbharu.hideout.domain.model.hideout.entity.User
import org.springframework.stereotype.Repository
@Repository
interface FollowerQueryService {
suspend fun findFollowersById(id: Long): List<User>
suspend fun findFollowersByNameAndDomain(name: String, domain: String): List<User>

View File

@ -0,0 +1,8 @@
package dev.usbharu.hideout.query.activitypub
import dev.usbharu.hideout.domain.model.ap.Note
import dev.usbharu.hideout.domain.model.hideout.entity.Post
interface NoteQueryService {
suspend fun findById(id: Long): Pair<Note, Post>
}

View File

@ -0,0 +1,39 @@
package dev.usbharu.hideout.query.activitypub
import dev.usbharu.hideout.domain.model.ap.Note
import dev.usbharu.hideout.domain.model.hideout.entity.Post
import dev.usbharu.hideout.exception.FailedToGetResourcesException
import dev.usbharu.hideout.repository.Posts
import dev.usbharu.hideout.repository.Users
import dev.usbharu.hideout.repository.toPost
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.select
import org.springframework.stereotype.Repository
import java.time.Instant
@Repository
class NoteQueryServiceImpl : NoteQueryService {
override suspend fun findById(id: Long): Pair<Note, Post> {
return Posts
.leftJoin(Users)
.select { Posts.id eq id }
.singleOr { FailedToGetResourcesException("id $id is duplicate or does not exist.") }
.let { it.toNote() to it.toPost() }
}
private fun ResultRow.toNote(): Note {
return Note(
name = "Post",
id = this[Posts.apId],
attributedTo = this[Users.url],
content = this[Posts.text],
published = Instant.ofEpochMilli(this[Posts.createdAt]).toString(),
to = listOf(),
cc = listOf(),
inReplyTo = null,
sensitive = false
)
}
}

View File

@ -0,0 +1,7 @@
package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.domain.model.ap.Note
interface NoteApApiService {
suspend fun getNote(postId: Long, userId: Long?): Note?
}

View File

@ -0,0 +1,37 @@
package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.domain.model.ap.Note
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
import dev.usbharu.hideout.query.FollowerQueryService
import dev.usbharu.hideout.query.activitypub.NoteQueryService
import dev.usbharu.hideout.service.core.Transaction
import org.springframework.stereotype.Service
@Service
class NoteApApiServiceImpl(
private val noteQueryService: NoteQueryService,
private val followerQueryService: FollowerQueryService,
private val transaction: Transaction
) : NoteApApiService {
override suspend fun getNote(postId: Long, userId: Long?): Note? = transaction.transaction {
val findById = noteQueryService.findById(postId)
when (findById.second.visibility) {
Visibility.PUBLIC, Visibility.UNLISTED -> {
return@transaction findById.first
}
Visibility.FOLLOWERS -> {
if (userId == null) {
return@transaction null
}
if (followerQueryService.alreadyFollow(findById.second.userId, userId).not()) {
return@transaction null
}
return@transaction findById.first
}
Visibility.DIRECT -> return@transaction null
}
}
}

View File

@ -7,6 +7,7 @@ import java.io.Serial
class HttpSignatureUser(
username: String,
val domain: String,
val id: Long,
credentialsNonExpired: Boolean,
accountNonLocked: Boolean,
authorities: MutableCollection<out GrantedAuthority>?
@ -19,6 +20,26 @@ class HttpSignatureUser(
accountNonLocked,
authorities
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is HttpSignatureUser) return false
if (!super.equals(other)) return false
if (domain != other.domain) return false
if (id != other.id) return false
return true
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + domain.hashCode()
result = 31 * result + id.hashCode()
return result
}
companion object {
@Serial
private const val serialVersionUID: Long = -3330552099960982997L

View File

@ -49,6 +49,7 @@ class HttpSignatureUserDetailsService(
HttpSignatureUser(
username = findByKeyId.name,
domain = findByKeyId.domain,
id = findByKeyId.id,
credentialsNonExpired = true,
accountNonLocked = true,
authorities = mutableListOf()