mirror of https://github.com/usbharu/Hideout.git
fix: 連合関係のエラーを修正
This commit is contained in:
parent
9fa26375d9
commit
34333b1a2d
|
@ -8,6 +8,8 @@ open class Person : Object {
|
||||||
var url: String? = null
|
var url: String? = null
|
||||||
private var icon: Image? = null
|
private var icon: Image? = null
|
||||||
var publicKey: Key? = null
|
var publicKey: Key? = null
|
||||||
|
var endpoints: Map<String, String> = emptyMap()
|
||||||
|
|
||||||
|
|
||||||
protected constructor() : super()
|
protected constructor() : super()
|
||||||
|
|
||||||
|
@ -22,7 +24,8 @@ open class Person : Object {
|
||||||
outbox: String?,
|
outbox: String?,
|
||||||
url: String?,
|
url: String?,
|
||||||
icon: Image?,
|
icon: Image?,
|
||||||
publicKey: Key?
|
publicKey: Key?,
|
||||||
|
endpoints: Map<String, String> = emptyMap()
|
||||||
) : super(add(type, "Person"), name, id = id) {
|
) : super(add(type, "Person"), name, id = id) {
|
||||||
this.preferredUsername = preferredUsername
|
this.preferredUsername = preferredUsername
|
||||||
this.summary = summary
|
this.summary = summary
|
||||||
|
@ -31,24 +34,28 @@ open class Person : Object {
|
||||||
this.url = url
|
this.url = url
|
||||||
this.icon = icon
|
this.icon = icon
|
||||||
this.publicKey = publicKey
|
this.publicKey = publicKey
|
||||||
|
this.endpoints = endpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other !is Person) return false
|
if (other !is Person) return false
|
||||||
|
if (!super.equals(other)) return false
|
||||||
|
|
||||||
if (id != other.id) return false
|
|
||||||
if (preferredUsername != other.preferredUsername) return false
|
if (preferredUsername != other.preferredUsername) return false
|
||||||
if (summary != other.summary) return false
|
if (summary != other.summary) return false
|
||||||
if (inbox != other.inbox) return false
|
if (inbox != other.inbox) return false
|
||||||
if (outbox != other.outbox) return false
|
if (outbox != other.outbox) return false
|
||||||
if (url != other.url) return false
|
if (url != other.url) return false
|
||||||
if (icon != other.icon) return false
|
if (icon != other.icon) return false
|
||||||
return publicKey == other.publicKey
|
if (publicKey != other.publicKey) return false
|
||||||
|
if (endpoints != other.endpoints) return false
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = id?.hashCode() ?: 0
|
var result = super.hashCode()
|
||||||
result = 31 * result + (preferredUsername?.hashCode() ?: 0)
|
result = 31 * result + (preferredUsername?.hashCode() ?: 0)
|
||||||
result = 31 * result + (summary?.hashCode() ?: 0)
|
result = 31 * result + (summary?.hashCode() ?: 0)
|
||||||
result = 31 * result + (inbox?.hashCode() ?: 0)
|
result = 31 * result + (inbox?.hashCode() ?: 0)
|
||||||
|
@ -56,6 +63,8 @@ open class Person : Object {
|
||||||
result = 31 * result + (url?.hashCode() ?: 0)
|
result = 31 * result + (url?.hashCode() ?: 0)
|
||||||
result = 31 * result + (icon?.hashCode() ?: 0)
|
result = 31 * result + (icon?.hashCode() ?: 0)
|
||||||
result = 31 * result + (publicKey?.hashCode() ?: 0)
|
result = 31 * result + (publicKey?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + endpoints.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dev.usbharu.hideout.exception
|
||||||
|
|
||||||
|
open class FailedToGetResourcesException : IllegalArgumentException {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(s: String?) : super(s)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
}
|
|
@ -7,16 +7,18 @@ import io.ktor.server.plugins.statuspages.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
|
|
||||||
fun Application.configureStatusPages() {
|
fun Application.configureStatusPages() {
|
||||||
|
|
||||||
install(StatusPages) {
|
install(StatusPages) {
|
||||||
exception<IllegalArgumentException> { call, cause ->
|
exception<IllegalArgumentException> { call, cause ->
|
||||||
call.respondText(text = "400: $cause", status = HttpStatusCode.BadRequest)
|
call.respondText(text = "400: $cause", status = HttpStatusCode.BadRequest)
|
||||||
|
call.application.log.warn("Bad Request", cause)
|
||||||
}
|
}
|
||||||
exception<InvalidUsernameOrPasswordException> { call, _ ->
|
exception<InvalidUsernameOrPasswordException> { call, _ ->
|
||||||
call.respond(HttpStatusCode.Unauthorized)
|
call.respond(HttpStatusCode.Unauthorized)
|
||||||
}
|
}
|
||||||
exception<Throwable> { call, cause ->
|
exception<Throwable> { call, cause ->
|
||||||
call.respondText(text = "500: ${cause.stackTraceToString()}", status = HttpStatusCode.InternalServerError)
|
call.respondText(text = "500: ${cause.stackTraceToString()}", status = HttpStatusCode.InternalServerError)
|
||||||
cause.printStackTrace()
|
call.application.log.error("Internal Server Error", cause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,4 +9,5 @@ interface FollowerQueryService {
|
||||||
suspend fun findFollowingByNameAndDomain(name: String, domain: String): List<User>
|
suspend fun findFollowingByNameAndDomain(name: String, domain: String): List<User>
|
||||||
suspend fun appendFollower(user: Long, follower: Long)
|
suspend fun appendFollower(user: Long, follower: Long)
|
||||||
suspend fun removeFollower(user: Long, follower: Long)
|
suspend fun removeFollower(user: Long, follower: Long)
|
||||||
|
suspend fun alreadyFollow(userId: Long, followerId: Long): Boolean
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,6 +198,12 @@ class FollowerQueryServiceImpl : FollowerQueryService {
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun removeFollower(user: Long, follower: Long) {
|
override suspend fun removeFollower(user: Long, follower: Long) {
|
||||||
UsersFollowers.deleteWhere { Users.id eq user and (followerId eq follower) }
|
UsersFollowers.deleteWhere { userId eq user and (followerId eq follower) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun alreadyFollow(userId: Long, followerId: Long): Boolean {
|
||||||
|
return UsersFollowers.select { UsersFollowers.userId eq userId or (UsersFollowers.followerId eq followerId) }
|
||||||
|
.empty()
|
||||||
|
.not()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package dev.usbharu.hideout.query
|
package dev.usbharu.hideout.query
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken
|
import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken
|
||||||
|
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||||
import dev.usbharu.hideout.repository.JwtRefreshTokens
|
import dev.usbharu.hideout.repository.JwtRefreshTokens
|
||||||
import dev.usbharu.hideout.repository.toJwtRefreshToken
|
import dev.usbharu.hideout.repository.toJwtRefreshToken
|
||||||
|
import dev.usbharu.hideout.util.singleOr
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.jetbrains.exposed.sql.deleteAll
|
import org.jetbrains.exposed.sql.deleteAll
|
||||||
import org.jetbrains.exposed.sql.deleteWhere
|
import org.jetbrains.exposed.sql.deleteWhere
|
||||||
|
@ -12,13 +14,19 @@ import org.koin.core.annotation.Single
|
||||||
@Single
|
@Single
|
||||||
class JwtRefreshTokenQueryServiceImpl : JwtRefreshTokenQueryService {
|
class JwtRefreshTokenQueryServiceImpl : JwtRefreshTokenQueryService {
|
||||||
override suspend fun findById(id: Long): JwtRefreshToken =
|
override suspend fun findById(id: Long): JwtRefreshToken =
|
||||||
JwtRefreshTokens.select { JwtRefreshTokens.id.eq(id) }.single().toJwtRefreshToken()
|
JwtRefreshTokens.select { JwtRefreshTokens.id.eq(id) }
|
||||||
|
.singleOr { FailedToGetResourcesException("id: $id is a duplicate or does not exist.", it) }
|
||||||
|
.toJwtRefreshToken()
|
||||||
|
|
||||||
override suspend fun findByToken(token: String): JwtRefreshToken =
|
override suspend fun findByToken(token: String): JwtRefreshToken =
|
||||||
JwtRefreshTokens.select { JwtRefreshTokens.refreshToken.eq(token) }.single().toJwtRefreshToken()
|
JwtRefreshTokens.select { JwtRefreshTokens.refreshToken.eq(token) }
|
||||||
|
.singleOr { FailedToGetResourcesException("token: $token is a duplicate or does not exist.", it) }
|
||||||
|
.toJwtRefreshToken()
|
||||||
|
|
||||||
override suspend fun findByUserId(userId: Long): JwtRefreshToken =
|
override suspend fun findByUserId(userId: Long): JwtRefreshToken =
|
||||||
JwtRefreshTokens.select { JwtRefreshTokens.userId.eq(userId) }.single().toJwtRefreshToken()
|
JwtRefreshTokens.select { JwtRefreshTokens.userId.eq(userId) }
|
||||||
|
.singleOr { FailedToGetResourcesException("userId: $userId is a duplicate or does not exist.", it) }
|
||||||
|
.toJwtRefreshToken()
|
||||||
|
|
||||||
override suspend fun deleteById(id: Long) {
|
override suspend fun deleteById(id: Long) {
|
||||||
JwtRefreshTokens.deleteWhere { JwtRefreshTokens.id eq id }
|
JwtRefreshTokens.deleteWhere { JwtRefreshTokens.id eq id }
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
package dev.usbharu.hideout.query
|
package dev.usbharu.hideout.query
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
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.Posts
|
||||||
import dev.usbharu.hideout.repository.toPost
|
import dev.usbharu.hideout.repository.toPost
|
||||||
|
import dev.usbharu.hideout.util.singleOr
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.koin.core.annotation.Single
|
import org.koin.core.annotation.Single
|
||||||
|
|
||||||
@Single
|
@Single
|
||||||
class PostQueryServiceImpl : PostQueryService {
|
class PostQueryServiceImpl : PostQueryService {
|
||||||
override suspend fun findById(id: Long): Post = Posts.select { Posts.id eq id }.single().toPost()
|
override suspend fun findById(id: Long): Post =
|
||||||
|
Posts.select { Posts.id eq id }
|
||||||
|
.singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) }.toPost()
|
||||||
|
|
||||||
override suspend fun findByUrl(url: String): Post = Posts.select { Posts.url eq url }.single().toPost()
|
override suspend fun findByUrl(url: String): Post = Posts.select { Posts.url eq url }
|
||||||
|
.singleOr { FailedToGetResourcesException("url: $url is duplicate or does not exist.", it) }.toPost()
|
||||||
|
|
||||||
override suspend fun findByApId(string: String): Post = Posts.select { Posts.apId eq string }.single().toPost()
|
override suspend fun findByApId(string: String): Post = Posts.select { Posts.apId eq string }
|
||||||
|
.singleOr { FailedToGetResourcesException("apId: $string is duplicate or does not exist.", it) }.toPost()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package dev.usbharu.hideout.query
|
package dev.usbharu.hideout.query
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse
|
import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse
|
||||||
|
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||||
import dev.usbharu.hideout.repository.Posts
|
import dev.usbharu.hideout.repository.Posts
|
||||||
import dev.usbharu.hideout.repository.Users
|
import dev.usbharu.hideout.repository.Users
|
||||||
import dev.usbharu.hideout.repository.toPost
|
import dev.usbharu.hideout.repository.toPost
|
||||||
import dev.usbharu.hideout.repository.toUser
|
import dev.usbharu.hideout.repository.toUser
|
||||||
|
import dev.usbharu.hideout.util.singleOr
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.innerJoin
|
import org.jetbrains.exposed.sql.innerJoin
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
|
@ -17,7 +19,7 @@ class PostResponseQueryServiceImpl : PostResponseQueryService {
|
||||||
return Posts
|
return Posts
|
||||||
.innerJoin(Users, onColumn = { Posts.userId }, otherColumn = { Users.id })
|
.innerJoin(Users, onColumn = { Posts.userId }, otherColumn = { Users.id })
|
||||||
.select { Posts.id eq id }
|
.select { Posts.id eq id }
|
||||||
.single()
|
.singleOr { FailedToGetResourcesException("id: $id,userId: $userId is a duplicate or does not exist.", it) }
|
||||||
.let { PostResponse.from(it.toPost(), it.toUser()) }
|
.let { PostResponse.from(it.toPost(), it.toUser()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,11 @@ package dev.usbharu.hideout.query
|
||||||
import dev.usbharu.hideout.domain.model.hideout.dto.Account
|
import dev.usbharu.hideout.domain.model.hideout.dto.Account
|
||||||
import dev.usbharu.hideout.domain.model.hideout.dto.ReactionResponse
|
import dev.usbharu.hideout.domain.model.hideout.dto.ReactionResponse
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.Reaction
|
import dev.usbharu.hideout.domain.model.hideout.entity.Reaction
|
||||||
|
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||||
import dev.usbharu.hideout.repository.Reactions
|
import dev.usbharu.hideout.repository.Reactions
|
||||||
import dev.usbharu.hideout.repository.Users
|
import dev.usbharu.hideout.repository.Users
|
||||||
import dev.usbharu.hideout.repository.toReaction
|
import dev.usbharu.hideout.repository.toReaction
|
||||||
|
import dev.usbharu.hideout.util.singleOr
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.koin.core.annotation.Single
|
import org.koin.core.annotation.Single
|
||||||
|
@ -26,7 +28,12 @@ class ReactionQueryServiceImpl : ReactionQueryService {
|
||||||
Reactions.emojiId.eq(emojiId)
|
Reactions.emojiId.eq(emojiId)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.single()
|
.singleOr {
|
||||||
|
FailedToGetResourcesException(
|
||||||
|
"postId: $postId,userId: $userId,emojiId: $emojiId is duplicate or does not exist.",
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
.toReaction()
|
.toReaction()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package dev.usbharu.hideout.query
|
package dev.usbharu.hideout.query
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
|
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||||
import dev.usbharu.hideout.repository.Users
|
import dev.usbharu.hideout.repository.Users
|
||||||
import dev.usbharu.hideout.repository.toUser
|
import dev.usbharu.hideout.repository.toUser
|
||||||
|
import dev.usbharu.hideout.util.singleOr
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
|
@ -13,14 +15,22 @@ class UserQueryServiceImpl : UserQueryService {
|
||||||
override suspend fun findAll(limit: Int, offset: Long): List<User> =
|
override suspend fun findAll(limit: Int, offset: Long): List<User> =
|
||||||
Users.selectAll().limit(limit, offset).map { it.toUser() }
|
Users.selectAll().limit(limit, offset).map { it.toUser() }
|
||||||
|
|
||||||
override suspend fun findById(id: Long): User = Users.select { Users.id eq id }.single().toUser()
|
override suspend fun findById(id: Long): User = Users.select { Users.id eq id }
|
||||||
|
.singleOr { FailedToGetResourcesException("id: $id is duplicate or does not exist.", it) }.toUser()
|
||||||
|
|
||||||
override suspend fun findByName(name: String): List<User> = Users.select { Users.name eq name }.map { it.toUser() }
|
override suspend fun findByName(name: String): List<User> = Users.select { Users.name eq name }.map { it.toUser() }
|
||||||
|
|
||||||
override suspend fun findByNameAndDomain(name: String, domain: String): User =
|
override suspend fun findByNameAndDomain(name: String, domain: String): User =
|
||||||
Users.select { Users.name eq name and (Users.domain eq domain) }.single().toUser()
|
Users
|
||||||
|
.select { Users.name eq name and (Users.domain eq domain) }
|
||||||
|
.singleOr {
|
||||||
|
FailedToGetResourcesException("name: $name,domain: $domain is duplicate or does not exist.", it)
|
||||||
|
}
|
||||||
|
.toUser()
|
||||||
|
|
||||||
override suspend fun findByUrl(url: String): User = Users.select { Users.url eq url }.single().toUser()
|
override suspend fun findByUrl(url: String): User = Users.select { Users.url eq url }
|
||||||
|
.singleOr { FailedToGetResourcesException("url: $url is duplicate or does not exist.", it) }
|
||||||
|
.toUser()
|
||||||
|
|
||||||
override suspend fun findByIds(ids: List<Long>): List<User> =
|
override suspend fun findByIds(ids: List<Long>): List<User> =
|
||||||
Users.select { Users.id inList ids }.map { it.toUser() }
|
Users.select { Users.id inList ids }.map { it.toUser() }
|
||||||
|
|
|
@ -2,6 +2,7 @@ package dev.usbharu.hideout.repository
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
||||||
|
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||||
import dev.usbharu.hideout.service.core.IdGenerateService
|
import dev.usbharu.hideout.service.core.IdGenerateService
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
@ -53,7 +54,8 @@ class PostRepositoryImpl(database: Database, private val idGenerateService: IdGe
|
||||||
return post
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findById(id: Long): Post = Posts.select { Posts.id eq id }.single().toPost()
|
override suspend fun findById(id: Long): Post = Posts.select { Posts.id eq id }.singleOrNull()?.toPost()
|
||||||
|
?: throw FailedToGetResourcesException("id: $id was not found.")
|
||||||
|
|
||||||
override suspend fun delete(id: Long) {
|
override suspend fun delete(id: Long) {
|
||||||
Posts.deleteWhere { Posts.id eq id }
|
Posts.deleteWhere { Posts.id eq id }
|
||||||
|
|
|
@ -41,7 +41,12 @@ fun Routing.usersAP(
|
||||||
?: throw ParameterNotExistException("Parameter(name='name') does not exist."),
|
?: throw ParameterNotExistException("Parameter(name='name') does not exist."),
|
||||||
Config.configData.domain
|
Config.configData.domain
|
||||||
)
|
)
|
||||||
call.respondText(userEntity.toString() + "\n" + followerQueryService.findFollowersById(userEntity.id))
|
val personByName = apUserService.getPersonByName(userEntity.name)
|
||||||
|
call.respondText(
|
||||||
|
userEntity.toString() + "\n" + followerQueryService.findFollowersById(userEntity.id) + "\n" + Config.configData.objectMapper.writeValueAsString(
|
||||||
|
personByName
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,11 @@ fun Route.users(userService: UserService, userApiService: UserApiService) {
|
||||||
authenticate(TOKEN_AUTH, optional = true) {
|
authenticate(TOKEN_AUTH, optional = true) {
|
||||||
get {
|
get {
|
||||||
val userParameter = (
|
val userParameter = (
|
||||||
call.parameters["name"]
|
call.parameters["name"]
|
||||||
?: throw ParameterNotExistException(
|
?: throw ParameterNotExistException(
|
||||||
"Parameter(name='userName@domain') does not exist."
|
"Parameter(name='userName@domain') does not exist."
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
if (userParameter.toLongOrNull() != null) {
|
if (userParameter.toLongOrNull() != null) {
|
||||||
return@get call.respond(userApiService.findById(userParameter.toLong()))
|
return@get call.respond(userApiService.findById(userParameter.toLong()))
|
||||||
} else {
|
} else {
|
||||||
|
@ -72,19 +72,16 @@ fun Route.users(userService: UserService, userApiService: UserApiService) {
|
||||||
?: throw IllegalStateException("no principal")
|
?: throw IllegalStateException("no principal")
|
||||||
val userParameter = call.parameters["name"]
|
val userParameter = call.parameters["name"]
|
||||||
?: throw ParameterNotExistException("Parameter(name='userName@domain') does not exist.")
|
?: throw ParameterNotExistException("Parameter(name='userName@domain') does not exist.")
|
||||||
if (userParameter.toLongOrNull() != null) {
|
if (if (userParameter.toLongOrNull() != null) {
|
||||||
if (userService.followRequest(userParameter.toLong(), userId)) {
|
userApiService.follow(userParameter.toLong(), userId)
|
||||||
return@post call.respond(HttpStatusCode.OK)
|
|
||||||
} else {
|
} else {
|
||||||
return@post call.respond(HttpStatusCode.Accepted)
|
val parse = AcctUtil.parse(userParameter)
|
||||||
|
userApiService.follow(parse, userId)
|
||||||
}
|
}
|
||||||
}
|
) {
|
||||||
val acct = AcctUtil.parse(userParameter)
|
call.respond(HttpStatusCode.OK)
|
||||||
val targetUser = userApiService.findByAcct(acct)
|
|
||||||
if (userService.followRequest(targetUser.id.toLong(), userId)) {
|
|
||||||
return@post call.respond(HttpStatusCode.OK)
|
|
||||||
} else {
|
} else {
|
||||||
return@post call.respond(HttpStatusCode.Accepted)
|
call.respond(HttpStatusCode.Accepted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,11 +89,11 @@ fun Route.users(userService: UserService, userApiService: UserApiService) {
|
||||||
route("/following") {
|
route("/following") {
|
||||||
get {
|
get {
|
||||||
val userParameter = (
|
val userParameter = (
|
||||||
call.parameters["name"]
|
call.parameters["name"]
|
||||||
?: throw ParameterNotExistException(
|
?: throw ParameterNotExistException(
|
||||||
"Parameter(name='userName@domain') does not exist."
|
"Parameter(name='userName@domain') does not exist."
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
if (userParameter.toLongOrNull() != null) {
|
if (userParameter.toLongOrNull() != null) {
|
||||||
return@get call.respond(userApiService.findFollowings(userParameter.toLong()))
|
return@get call.respond(userApiService.findFollowings(userParameter.toLong()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
|
||||||
import dev.usbharu.hideout.domain.model.ap.Accept
|
import dev.usbharu.hideout.domain.model.ap.Accept
|
||||||
import dev.usbharu.hideout.domain.model.ap.Follow
|
import dev.usbharu.hideout.domain.model.ap.Follow
|
||||||
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
||||||
|
import dev.usbharu.hideout.query.FollowerQueryService
|
||||||
import dev.usbharu.hideout.query.UserQueryService
|
import dev.usbharu.hideout.query.UserQueryService
|
||||||
|
import dev.usbharu.hideout.service.core.Transaction
|
||||||
import dev.usbharu.hideout.service.user.UserService
|
import dev.usbharu.hideout.service.user.UserService
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import org.koin.core.annotation.Single
|
import org.koin.core.annotation.Single
|
||||||
|
@ -17,20 +19,27 @@ interface APAcceptService {
|
||||||
@Single
|
@Single
|
||||||
class APAcceptServiceImpl(
|
class APAcceptServiceImpl(
|
||||||
private val userService: UserService,
|
private val userService: UserService,
|
||||||
private val userQueryService: UserQueryService
|
private val userQueryService: UserQueryService,
|
||||||
|
private val followerQueryService: FollowerQueryService,
|
||||||
|
private val transaction: Transaction
|
||||||
) : APAcceptService {
|
) : APAcceptService {
|
||||||
override suspend fun receiveAccept(accept: Accept): ActivityPubResponse {
|
override suspend fun receiveAccept(accept: Accept): ActivityPubResponse {
|
||||||
val value = accept.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
return transaction.transaction {
|
||||||
if (value.type.contains("Follow").not()) {
|
val value = accept.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
||||||
throw IllegalActivityPubObjectException("Invalid type ${value.type}")
|
if (value.type.contains("Follow").not()) {
|
||||||
}
|
throw IllegalActivityPubObjectException("Invalid type ${value.type}")
|
||||||
|
}
|
||||||
|
|
||||||
val follow = value as Follow
|
val follow = value as Follow
|
||||||
val userUrl = follow.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
val userUrl = follow.`object` ?: throw IllegalActivityPubObjectException("object is null")
|
||||||
val followerUrl = follow.actor ?: throw IllegalActivityPubObjectException("actor is null")
|
val followerUrl = follow.actor ?: throw IllegalActivityPubObjectException("actor is null")
|
||||||
val user = userQueryService.findByUrl(userUrl)
|
val user = userQueryService.findByUrl(userUrl)
|
||||||
val follower = userQueryService.findByUrl(followerUrl)
|
val follower = userQueryService.findByUrl(followerUrl)
|
||||||
userService.follow(user.id, follower.id)
|
if (followerQueryService.alreadyFollow(user.id, follower.id)) {
|
||||||
return ActivityPubStringResponse(HttpStatusCode.OK, "accepted")
|
return@transaction ActivityPubStringResponse(HttpStatusCode.OK, "accepted")
|
||||||
|
}
|
||||||
|
userService.follow(user.id, follower.id)
|
||||||
|
ActivityPubStringResponse(HttpStatusCode.OK, "accepted")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import dev.usbharu.hideout.domain.model.ap.Note
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
import dev.usbharu.hideout.domain.model.hideout.entity.Post
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
||||||
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
|
||||||
|
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||||
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
||||||
import dev.usbharu.hideout.plugins.getAp
|
import dev.usbharu.hideout.plugins.getAp
|
||||||
import dev.usbharu.hideout.plugins.postAp
|
import dev.usbharu.hideout.plugins.postAp
|
||||||
|
@ -83,11 +84,10 @@ class APNoteServiceImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchNote(url: String, targetActor: String?): Note {
|
override suspend fun fetchNote(url: String, targetActor: String?): Note {
|
||||||
val post = postQueryService.findByUrl(url)
|
|
||||||
try {
|
try {
|
||||||
|
val post = postQueryService.findByUrl(url)
|
||||||
return postToNote(post)
|
return postToNote(post)
|
||||||
} catch (_: NoSuchElementException) {
|
} catch (_: FailedToGetResourcesException) {
|
||||||
} catch (_: IllegalArgumentException) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val response = httpClient.getAp(
|
val response = httpClient.getAp(
|
||||||
|
@ -125,21 +125,18 @@ class APNoteServiceImpl(
|
||||||
|
|
||||||
val findByApId = try {
|
val findByApId = try {
|
||||||
postQueryService.findByApId(note.id!!)
|
postQueryService.findByApId(note.id!!)
|
||||||
} catch (_: NoSuchElementException) {
|
} catch (_: FailedToGetResourcesException) {
|
||||||
return internalNote(note, targetActor, url)
|
|
||||||
} catch (_: IllegalArgumentException) {
|
|
||||||
return internalNote(note, targetActor, url)
|
return internalNote(note, targetActor, url)
|
||||||
}
|
}
|
||||||
return postToNote(findByApId)
|
return postToNote(findByApId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun internalNote(note: Note, targetActor: String?, url: String): Note {
|
private suspend fun internalNote(note: Note, targetActor: String?, url: String): Note {
|
||||||
val person = apUserService.fetchPerson(
|
val person = apUserService.fetchPersonWithEntity(
|
||||||
note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"),
|
note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"),
|
||||||
targetActor
|
targetActor
|
||||||
)
|
)
|
||||||
val user =
|
|
||||||
userQueryService.findByUrl(person.url ?: throw IllegalActivityPubObjectException("person.url is null"))
|
|
||||||
|
|
||||||
val visibility =
|
val visibility =
|
||||||
if (note.to.contains(public) && note.cc.contains(public)) {
|
if (note.to.contains(public) && note.cc.contains(public)) {
|
||||||
|
@ -160,7 +157,7 @@ class APNoteServiceImpl(
|
||||||
postRepository.save(
|
postRepository.save(
|
||||||
Post(
|
Post(
|
||||||
id = postRepository.generateId(),
|
id = postRepository.generateId(),
|
||||||
userId = user.id,
|
userId = person.second.id,
|
||||||
overview = null,
|
overview = null,
|
||||||
text = note.content.orEmpty(),
|
text = note.content.orEmpty(),
|
||||||
createdAt = Instant.parse(note.published).toEpochMilli(),
|
createdAt = Instant.parse(note.published).toEpochMilli(),
|
||||||
|
|
|
@ -6,6 +6,8 @@ import dev.usbharu.hideout.domain.model.ap.Image
|
||||||
import dev.usbharu.hideout.domain.model.ap.Key
|
import dev.usbharu.hideout.domain.model.ap.Key
|
||||||
import dev.usbharu.hideout.domain.model.ap.Person
|
import dev.usbharu.hideout.domain.model.ap.Person
|
||||||
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
|
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
|
||||||
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
|
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||||
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
|
||||||
import dev.usbharu.hideout.plugins.getAp
|
import dev.usbharu.hideout.plugins.getAp
|
||||||
import dev.usbharu.hideout.query.UserQueryService
|
import dev.usbharu.hideout.query.UserQueryService
|
||||||
|
@ -29,6 +31,8 @@ interface APUserService {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
suspend fun fetchPerson(url: String, targetActor: String? = null): Person
|
suspend fun fetchPerson(url: String, targetActor: String? = null): Person
|
||||||
|
|
||||||
|
suspend fun fetchPersonWithEntity(url: String, targetActor: String? = null): Pair<Person, User>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Single
|
@Single
|
||||||
|
@ -67,11 +71,15 @@ class APUserServiceImpl(
|
||||||
id = "$userUrl#pubkey",
|
id = "$userUrl#pubkey",
|
||||||
owner = userUrl,
|
owner = userUrl,
|
||||||
publicKeyPem = userEntity.publicKey
|
publicKeyPem = userEntity.publicKey
|
||||||
)
|
),
|
||||||
|
endpoints = mapOf("sharedInbox" to "${Config.configData.url}/inbox")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchPerson(url: String, targetActor: String?): Person {
|
override suspend fun fetchPerson(url: String, targetActor: String?): Person =
|
||||||
|
fetchPersonWithEntity(url, targetActor).first
|
||||||
|
|
||||||
|
override suspend fun fetchPersonWithEntity(url: String, targetActor: String?): Pair<Person, User> {
|
||||||
return try {
|
return try {
|
||||||
val userEntity = userQueryService.findByUrl(url)
|
val userEntity = userQueryService.findByUrl(url)
|
||||||
return Person(
|
return Person(
|
||||||
|
@ -95,9 +103,10 @@ class APUserServiceImpl(
|
||||||
id = "$url#pubkey",
|
id = "$url#pubkey",
|
||||||
owner = url,
|
owner = url,
|
||||||
publicKeyPem = userEntity.publicKey
|
publicKeyPem = userEntity.publicKey
|
||||||
)
|
),
|
||||||
)
|
endpoints = mapOf("sharedInbox" to "${Config.configData.url}/inbox")
|
||||||
} catch (ignore: NoSuchElementException) {
|
) to userEntity
|
||||||
|
} catch (ignore: FailedToGetResourcesException) {
|
||||||
val httpResponse = if (targetActor != null) {
|
val httpResponse = if (targetActor != null) {
|
||||||
httpClient.getAp(url, "$targetActor#pubkey")
|
httpClient.getAp(url, "$targetActor#pubkey")
|
||||||
} else {
|
} else {
|
||||||
|
@ -107,7 +116,7 @@ class APUserServiceImpl(
|
||||||
}
|
}
|
||||||
val person = Config.configData.objectMapper.readValue<Person>(httpResponse.bodyAsText())
|
val person = Config.configData.objectMapper.readValue<Person>(httpResponse.bodyAsText())
|
||||||
|
|
||||||
userService.createRemoteUser(
|
person to userService.createRemoteUser(
|
||||||
RemoteUserCreateDto(
|
RemoteUserCreateDto(
|
||||||
name = person.preferredUsername
|
name = person.preferredUsername
|
||||||
?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
||||||
|
@ -122,7 +131,6 @@ class APUserServiceImpl(
|
||||||
?: throw IllegalActivityPubObjectException("publicKey is null"),
|
?: throw IllegalActivityPubObjectException("publicKey is null"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
person
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,9 @@ interface UserApiService {
|
||||||
suspend fun findFollowingsByAcct(acct: Acct): List<UserResponse>
|
suspend fun findFollowingsByAcct(acct: Acct): List<UserResponse>
|
||||||
|
|
||||||
suspend fun createUser(username: String, password: String): UserResponse
|
suspend fun createUser(username: String, password: String): UserResponse
|
||||||
|
|
||||||
|
suspend fun follow(targetId: Long, sourceId: Long): Boolean
|
||||||
|
suspend fun follow(targetAcct: Acct, sourceId: Long): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@Single
|
@Single
|
||||||
|
@ -39,30 +42,50 @@ class UserApiServiceImpl(
|
||||||
private val userService: UserService,
|
private val userService: UserService,
|
||||||
private val transaction: Transaction
|
private val transaction: Transaction
|
||||||
) : UserApiService {
|
) : UserApiService {
|
||||||
override suspend fun findAll(limit: Int?, offset: Long): List<UserResponse> =
|
override suspend fun findAll(limit: Int?, offset: Long): List<UserResponse> = transaction.transaction {
|
||||||
userQueryService.findAll(min(limit ?: 100, 100), offset).map { UserResponse.from(it) }
|
userQueryService.findAll(min(limit ?: 100, 100), offset).map { UserResponse.from(it) }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun findById(id: Long): UserResponse = UserResponse.from(userQueryService.findById(id))
|
|
||||||
|
|
||||||
override suspend fun findByIds(ids: List<Long>): List<UserResponse> =
|
override suspend fun findById(id: Long): UserResponse =
|
||||||
userQueryService.findByIds(ids).map { UserResponse.from(it) }
|
transaction.transaction { UserResponse.from(userQueryService.findById(id)) }
|
||||||
|
|
||||||
override suspend fun findByAcct(acct: Acct): UserResponse =
|
override suspend fun findByIds(ids: List<Long>): List<UserResponse> {
|
||||||
UserResponse.from(userQueryService.findByNameAndDomain(acct.username, acct.domain ?: Config.configData.domain))
|
return transaction.transaction {
|
||||||
|
userQueryService.findByIds(ids).map { UserResponse.from(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun findFollowers(userId: Long): List<UserResponse> =
|
override suspend fun findByAcct(acct: Acct): UserResponse {
|
||||||
|
return transaction.transaction {
|
||||||
|
UserResponse.from(
|
||||||
|
userQueryService.findByNameAndDomain(
|
||||||
|
acct.username,
|
||||||
|
acct.domain ?: Config.configData.domain
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findFollowers(userId: Long): List<UserResponse> = transaction.transaction {
|
||||||
followerQueryService.findFollowersById(userId).map { UserResponse.from(it) }
|
followerQueryService.findFollowersById(userId).map { UserResponse.from(it) }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun findFollowings(userId: Long): List<UserResponse> =
|
|
||||||
|
override suspend fun findFollowings(userId: Long): List<UserResponse> = transaction.transaction {
|
||||||
followerQueryService.findFollowingById(userId).map { UserResponse.from(it) }
|
followerQueryService.findFollowingById(userId).map { UserResponse.from(it) }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun findFollowersByAcct(acct: Acct): List<UserResponse> =
|
override suspend fun findFollowersByAcct(acct: Acct): List<UserResponse> = transaction.transaction {
|
||||||
followerQueryService.findFollowersByNameAndDomain(acct.username, acct.domain ?: Config.configData.domain)
|
followerQueryService.findFollowersByNameAndDomain(acct.username, acct.domain ?: Config.configData.domain)
|
||||||
.map { UserResponse.from(it) }
|
.map { UserResponse.from(it) }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun findFollowingsByAcct(acct: Acct): List<UserResponse> =
|
override suspend fun findFollowingsByAcct(acct: Acct): List<UserResponse> = transaction.transaction {
|
||||||
followerQueryService.findFollowingByNameAndDomain(acct.username, acct.domain ?: Config.configData.domain)
|
followerQueryService.findFollowingByNameAndDomain(acct.username, acct.domain ?: Config.configData.domain)
|
||||||
.map { UserResponse.from(it) }
|
.map { UserResponse.from(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override suspend fun createUser(username: String, password: String): UserResponse {
|
override suspend fun createUser(username: String, password: String): UserResponse {
|
||||||
return transaction.transaction {
|
return transaction.transaction {
|
||||||
|
@ -72,4 +95,21 @@ class UserApiServiceImpl(
|
||||||
UserResponse.from(userService.createLocalUser(UserCreateDto(username, username, "", password)))
|
UserResponse.from(userService.createLocalUser(UserCreateDto(username, username, "", password)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun follow(targetId: Long, sourceId: Long): Boolean {
|
||||||
|
return transaction.transaction {
|
||||||
|
userService.followRequest(targetId, sourceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun follow(targetAcct: Acct, sourceId: Long): Boolean {
|
||||||
|
return transaction.transaction {
|
||||||
|
userService.followRequest(
|
||||||
|
userQueryService.findByNameAndDomain(
|
||||||
|
targetAcct.username,
|
||||||
|
targetAcct.domain ?: Config.configData.domain
|
||||||
|
).id, sourceId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dev.usbharu.hideout.util
|
||||||
|
|
||||||
|
class CollectionUtil {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Iterable<T>.singleOr(block: (e: RuntimeException) -> Throwable): T {
|
||||||
|
return try {
|
||||||
|
this.single()
|
||||||
|
} catch (e: NoSuchElementException) {
|
||||||
|
throw block(e)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
throw block(e)
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
<root level="INFO">
|
<root level="TRACE">
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</root>
|
</root>
|
||||||
<logger name="org.eclipse.jetty" level="INFO"/>
|
<logger name="org.eclipse.jetty" level="INFO"/>
|
||||||
|
|
Loading…
Reference in New Issue