Merge pull request #30 from usbharu/feature/refactor-filename

Feature/refactor filename
This commit is contained in:
usbharu 2023-08-11 16:48:49 +09:00 committed by GitHub
commit 714ac201e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 1008 additions and 1074 deletions

View File

@ -15,17 +15,17 @@ import dev.usbharu.hideout.plugins.*
import dev.usbharu.hideout.query.FollowerQueryService import dev.usbharu.hideout.query.FollowerQueryService
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.routing.register import dev.usbharu.hideout.routing.register
import dev.usbharu.hideout.service.activitypub.ActivityPubService import dev.usbharu.hideout.service.ap.APService
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService import dev.usbharu.hideout.service.ap.APUserService
import dev.usbharu.hideout.service.api.IPostApiService import dev.usbharu.hideout.service.api.PostApiService
import dev.usbharu.hideout.service.api.IUserApiService import dev.usbharu.hideout.service.api.UserApiService
import dev.usbharu.hideout.service.api.UserAuthApiService import dev.usbharu.hideout.service.api.UserAuthApiService
import dev.usbharu.hideout.service.api.WebFingerApiService import dev.usbharu.hideout.service.api.WebFingerApiService
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
import dev.usbharu.hideout.service.core.* import dev.usbharu.hideout.service.core.*
import dev.usbharu.hideout.service.job.JobQueueParentService import dev.usbharu.hideout.service.job.JobQueueParentService
import dev.usbharu.hideout.service.job.KJobJobQueueParentService import dev.usbharu.hideout.service.job.KJobJobQueueParentService
import dev.usbharu.hideout.service.user.IUserService import dev.usbharu.hideout.service.user.UserService
import dev.usbharu.kjob.exposed.ExposedKJob import dev.usbharu.kjob.exposed.ExposedKJob
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.engine.cio.* import io.ktor.client.engine.cio.*
@ -94,26 +94,26 @@ fun Application.parent() {
configureKoin(module, HideoutModule().module) configureKoin(module, HideoutModule().module)
configureStatusPages() configureStatusPages()
runBlocking { runBlocking {
inject<IServerInitialiseService>().value.init() inject<ServerInitialiseService>().value.init()
} }
configureCompression() configureCompression()
configureHTTP() configureHTTP()
configureStaticRouting() configureStaticRouting()
configureMonitoring() configureMonitoring()
configureSerialization() configureSerialization()
register(inject<IUserApiService>().value) register(inject<UserApiService>().value)
configureSecurity( configureSecurity(
inject<JwkProvider>().value, inject<JwkProvider>().value,
inject<IMetaService>().value inject<MetaService>().value
) )
configureRouting( configureRouting(
httpSignatureVerifyService = inject<HttpSignatureVerifyService>().value, httpSignatureVerifyService = inject<HttpSignatureVerifyService>().value,
activityPubService = inject<ActivityPubService>().value, apService = inject<APService>().value,
userService = inject<IUserService>().value, userService = inject<UserService>().value,
activityPubUserService = inject<ActivityPubUserService>().value, apUserService = inject<APUserService>().value,
postService = inject<IPostApiService>().value, postService = inject<PostApiService>().value,
userApiService = inject<IUserApiService>().value, userApiService = inject<UserApiService>().value,
userQueryService = inject<UserQueryService>().value, userQueryService = inject<UserQueryService>().value,
followerQueryService = inject<FollowerQueryService>().value, followerQueryService = inject<FollowerQueryService>().value,
userAuthApiService = inject<UserAuthApiService>().value, userAuthApiService = inject<UserAuthApiService>().value,
@ -128,28 +128,28 @@ fun Application.worker() {
connectionDatabase = inject<Database>().value connectionDatabase = inject<Database>().value
}.start() }.start()
val activityPubService = inject<ActivityPubService>().value val apService = inject<APService>().value
kJob.register(ReceiveFollowJob) { kJob.register(ReceiveFollowJob) {
execute { execute {
activityPubService.processActivity(this, it) apService.processActivity(this, it)
} }
} }
kJob.register(DeliverPostJob) { kJob.register(DeliverPostJob) {
execute { execute {
activityPubService.processActivity(this, it) apService.processActivity(this, it)
} }
} }
kJob.register(DeliverReactionJob) { kJob.register(DeliverReactionJob) {
execute { execute {
activityPubService.processActivity(this, it) apService.processActivity(this, it)
} }
} }
kJob.register(DeliverRemoveReactionJob) { kJob.register(DeliverRemoveReactionJob) {
execute { execute {
activityPubService.processActivity(this, it) apService.processActivity(this, it)
} }
} }
} }

View File

@ -4,7 +4,7 @@ import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.JsonNode
import dev.usbharu.hideout.service.activitypub.ExtendedActivityVocabulary import dev.usbharu.hideout.service.ap.ExtendedActivityVocabulary
class ObjectDeserializer : JsonDeserializer<Object>() { class ObjectDeserializer : JsonDeserializer<Object>() {
@Suppress("LongMethod", "CyclomaticComplexMethod") @Suppress("LongMethod", "CyclomaticComplexMethod")

View File

@ -4,7 +4,7 @@ import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.ap.JsonLd import dev.usbharu.hideout.domain.model.ap.JsonLd
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.core.Transaction import dev.usbharu.hideout.service.core.Transaction
import dev.usbharu.hideout.service.user.UserAuthService import dev.usbharu.hideout.service.user.UserAuthServiceImpl
import dev.usbharu.hideout.util.HttpUtil.Activity import dev.usbharu.hideout.util.HttpUtil.Activity
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.plugins.api.* import io.ktor.client.plugins.api.*
@ -73,7 +73,7 @@ val httpSignaturePlugin = createClientPlugin("HttpSign", ::HttpSignaturePluginCo
println("Digest !!") println("Digest !!")
// UserAuthService.sha256.reset() // UserAuthService.sha256.reset()
val digest = val digest =
Base64.getEncoder().encodeToString(UserAuthService.sha256.digest(body.toByteArray(Charsets.UTF_8))) Base64.getEncoder().encodeToString(UserAuthServiceImpl.sha256.digest(body.toByteArray(Charsets.UTF_8)))
request.headers.append("Digest", "sha-256=$digest") request.headers.append("Digest", "sha-256=$digest")
} }

View File

@ -9,15 +9,15 @@ import dev.usbharu.hideout.routing.api.internal.v1.auth
import dev.usbharu.hideout.routing.api.internal.v1.posts import dev.usbharu.hideout.routing.api.internal.v1.posts
import dev.usbharu.hideout.routing.api.internal.v1.users import dev.usbharu.hideout.routing.api.internal.v1.users
import dev.usbharu.hideout.routing.wellknown.webfinger import dev.usbharu.hideout.routing.wellknown.webfinger
import dev.usbharu.hideout.service.activitypub.ActivityPubService import dev.usbharu.hideout.service.ap.APService
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService import dev.usbharu.hideout.service.ap.APUserService
import dev.usbharu.hideout.service.api.IPostApiService import dev.usbharu.hideout.service.api.PostApiService
import dev.usbharu.hideout.service.api.IUserApiService import dev.usbharu.hideout.service.api.UserApiService
import dev.usbharu.hideout.service.api.UserAuthApiService import dev.usbharu.hideout.service.api.UserAuthApiService
import dev.usbharu.hideout.service.api.WebFingerApiService import dev.usbharu.hideout.service.api.WebFingerApiService
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
import dev.usbharu.hideout.service.core.Transaction import dev.usbharu.hideout.service.core.Transaction
import dev.usbharu.hideout.service.user.IUserService import dev.usbharu.hideout.service.user.UserService
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.plugins.autohead.* import io.ktor.server.plugins.autohead.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
@ -25,11 +25,11 @@ import io.ktor.server.routing.*
@Suppress("LongParameterList") @Suppress("LongParameterList")
fun Application.configureRouting( fun Application.configureRouting(
httpSignatureVerifyService: HttpSignatureVerifyService, httpSignatureVerifyService: HttpSignatureVerifyService,
activityPubService: ActivityPubService, apService: APService,
userService: IUserService, userService: UserService,
activityPubUserService: ActivityPubUserService, apUserService: APUserService,
postService: IPostApiService, postService: PostApiService,
userApiService: IUserApiService, userApiService: UserApiService,
userQueryService: UserQueryService, userQueryService: UserQueryService,
followerQueryService: FollowerQueryService, followerQueryService: FollowerQueryService,
userAuthApiService: UserAuthApiService, userAuthApiService: UserAuthApiService,
@ -38,9 +38,9 @@ fun Application.configureRouting(
) { ) {
install(AutoHeadResponse) install(AutoHeadResponse)
routing { routing {
inbox(httpSignatureVerifyService, activityPubService) inbox(httpSignatureVerifyService, apService)
outbox() outbox()
usersAP(activityPubUserService, userQueryService, followerQueryService, transaction) usersAP(apUserService, userQueryService, followerQueryService, transaction)
webfinger(webFingerApiService) webfinger(webFingerApiService)
route("/api/internal/v1") { route("/api/internal/v1") {
posts(postService) posts(postService)

View File

@ -2,7 +2,7 @@ package dev.usbharu.hideout.plugins
import com.auth0.jwk.JwkProvider import com.auth0.jwk.JwkProvider
import dev.usbharu.hideout.config.Config import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.service.core.IMetaService import dev.usbharu.hideout.service.core.MetaService
import dev.usbharu.hideout.util.JsonWebKeyUtil import dev.usbharu.hideout.util.JsonWebKeyUtil
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
@ -16,7 +16,7 @@ const val TOKEN_AUTH = "jwt-auth"
@Suppress("MagicNumber") @Suppress("MagicNumber")
fun Application.configureSecurity( fun Application.configureSecurity(
jwkProvider: JwkProvider, jwkProvider: JwkProvider,
metaService: IMetaService metaService: MetaService
) { ) {
val issuer = Config.configData.url val issuer = Config.configData.url
install(Authentication) { install(Authentication) {

View File

@ -1,18 +0,0 @@
package dev.usbharu.hideout.repository
import dev.usbharu.hideout.domain.model.hideout.entity.User
@Suppress("TooManyFunctions")
interface IUserRepository {
suspend fun save(user: User): User
suspend fun findById(id: Long): User?
suspend fun delete(id: Long)
suspend fun deleteFollowRequest(id: Long, follower: Long)
suspend fun findFollowRequestsById(id: Long, follower: Long): Boolean
suspend fun nextId(): Long
}

View File

@ -2,7 +2,7 @@ package dev.usbharu.hideout.repository
import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken import dev.usbharu.hideout.domain.model.hideout.entity.JwtRefreshToken
interface IJwtRefreshTokenRepository { interface JwtRefreshTokenRepository {
suspend fun generateId(): Long suspend fun generateId(): Long
suspend fun save(token: JwtRefreshToken) suspend fun save(token: JwtRefreshToken)

View File

@ -13,7 +13,7 @@ class JwtRefreshTokenRepositoryImpl(
private val database: Database, private val database: Database,
private val idGenerateService: IdGenerateService private val idGenerateService: IdGenerateService
) : ) :
IJwtRefreshTokenRepository { JwtRefreshTokenRepository {
init { init {
transaction(database) { transaction(database) {

View File

@ -2,7 +2,7 @@ package dev.usbharu.hideout.repository
import dev.usbharu.hideout.domain.model.hideout.entity.Meta import dev.usbharu.hideout.domain.model.hideout.entity.Meta
interface IMetaRepository { interface MetaRepository {
suspend fun save(meta: Meta) suspend fun save(meta: Meta)

View File

@ -7,7 +7,7 @@ import org.koin.core.annotation.Single
import java.util.* import java.util.*
@Single @Single
class MetaRepositoryImpl(private val database: Database) : IMetaRepository { class MetaRepositoryImpl(private val database: Database) : MetaRepository {
init { init {
transaction(database) { transaction(database) {

View File

@ -3,7 +3,7 @@ package dev.usbharu.hideout.repository
import dev.usbharu.hideout.domain.model.hideout.entity.Post import dev.usbharu.hideout.domain.model.hideout.entity.Post
@Suppress("LongParameterList") @Suppress("LongParameterList")
interface IPostRepository { interface PostRepository {
suspend fun generateId(): Long suspend fun generateId(): Long
suspend fun save(post: Post): Post suspend fun save(post: Post): Post
suspend fun delete(id: Long) suspend fun delete(id: Long)

View File

@ -9,7 +9,7 @@ import org.jetbrains.exposed.sql.transactions.transaction
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
@Single @Single
class PostRepositoryImpl(database: Database, private val idGenerateService: IdGenerateService) : IPostRepository { class PostRepositoryImpl(database: Database, private val idGenerateService: IdGenerateService) : PostRepository {
init { init {
transaction(database) { transaction(database) {

View File

@ -1,137 +1,18 @@
package dev.usbharu.hideout.repository package dev.usbharu.hideout.repository
import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.service.core.IdGenerateService
import org.jetbrains.exposed.dao.id.LongIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction
import org.koin.core.annotation.Single
import java.time.Instant
@Single @Suppress("TooManyFunctions")
class UserRepository(private val database: Database, private val idGenerateService: IdGenerateService) : interface UserRepository {
IUserRepository { suspend fun save(user: User): User
init {
transaction(database) {
SchemaUtils.create(Users)
SchemaUtils.create(UsersFollowers)
SchemaUtils.createMissingTablesAndColumns(Users)
SchemaUtils.createMissingTablesAndColumns(UsersFollowers)
SchemaUtils.create(FollowRequests)
SchemaUtils.createMissingTablesAndColumns(FollowRequests)
}
}
override suspend fun save(user: User): User { suspend fun findById(id: Long): User?
val singleOrNull = Users.select { Users.id eq user.id }.singleOrNull()
if (singleOrNull == null) {
Users.insert {
it[id] = user.id
it[name] = user.name
it[domain] = user.domain
it[screenName] = user.screenName
it[description] = user.description
it[password] = user.password
it[inbox] = user.inbox
it[outbox] = user.outbox
it[url] = user.url
it[createdAt] = user.createdAt.toEpochMilli()
it[publicKey] = user.publicKey
it[privateKey] = user.privateKey
}
} else {
Users.update({ Users.id eq user.id }) {
it[name] = user.name
it[domain] = user.domain
it[screenName] = user.screenName
it[description] = user.description
it[password] = user.password
it[inbox] = user.inbox
it[outbox] = user.outbox
it[url] = user.url
it[createdAt] = user.createdAt.toEpochMilli()
it[publicKey] = user.publicKey
it[privateKey] = user.privateKey
}
}
return user
}
override suspend fun findById(id: Long): User? { suspend fun delete(id: Long)
return Users.select { Users.id eq id }.map {
it.toUser()
}.singleOrNull()
}
override suspend fun deleteFollowRequest(id: Long, follower: Long) { suspend fun deleteFollowRequest(id: Long, follower: Long)
FollowRequests.deleteWhere { userId.eq(id) and followerId.eq(follower) }
}
override suspend fun findFollowRequestsById(id: Long, follower: Long): Boolean { suspend fun findFollowRequestsById(id: Long, follower: Long): Boolean
return FollowRequests.select { (FollowRequests.userId eq id) and (FollowRequests.followerId eq follower) }
.singleOrNull() != null
}
override suspend fun delete(id: Long) { suspend fun nextId(): Long
Users.deleteWhere { Users.id.eq(id) }
}
override suspend fun nextId(): Long = idGenerateService.generateId()
}
object Users : Table("users") {
val id = long("id")
val name = varchar("name", length = 64)
val domain = varchar("domain", length = 255)
val screenName = varchar("screen_name", length = 64)
val description = varchar("description", length = 600)
val password = varchar("password", length = 255).nullable()
val inbox = varchar("inbox", length = 255).uniqueIndex()
val outbox = varchar("outbox", length = 255).uniqueIndex()
val url = varchar("url", length = 255).uniqueIndex()
val publicKey = varchar("public_key", length = 10000)
val privateKey = varchar("private_key", length = 10000).nullable()
val createdAt = long("created_at")
override val primaryKey: PrimaryKey = PrimaryKey(id)
init {
uniqueIndex(name, domain)
}
}
fun ResultRow.toUser(): User {
return User(
id = this[Users.id],
name = this[Users.name],
domain = this[Users.domain],
screenName = this[Users.screenName],
description = this[Users.description],
password = this[Users.password],
inbox = this[Users.inbox],
outbox = this[Users.outbox],
url = this[Users.url],
publicKey = this[Users.publicKey],
privateKey = this[Users.privateKey],
createdAt = Instant.ofEpochMilli((this[Users.createdAt]))
)
}
object UsersFollowers : LongIdTable("users_followers") {
val userId = long("user_id").references(Users.id).index()
val followerId = long("follower_id").references(Users.id)
init {
uniqueIndex(userId, followerId)
}
}
object FollowRequests : LongIdTable("follow_requests") {
val userId = long("user_id").references(Users.id)
val followerId = long("follower_id").references(Users.id)
init {
uniqueIndex(userId, followerId)
}
} }

View File

@ -0,0 +1,137 @@
package dev.usbharu.hideout.repository
import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.service.core.IdGenerateService
import org.jetbrains.exposed.dao.id.LongIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction
import org.koin.core.annotation.Single
import java.time.Instant
@Single
class UserRepositoryImpl(private val database: Database, private val idGenerateService: IdGenerateService) :
UserRepository {
init {
transaction(database) {
SchemaUtils.create(Users)
SchemaUtils.create(UsersFollowers)
SchemaUtils.createMissingTablesAndColumns(Users)
SchemaUtils.createMissingTablesAndColumns(UsersFollowers)
SchemaUtils.create(FollowRequests)
SchemaUtils.createMissingTablesAndColumns(FollowRequests)
}
}
override suspend fun save(user: User): User {
val singleOrNull = Users.select { Users.id eq user.id }.singleOrNull()
if (singleOrNull == null) {
Users.insert {
it[id] = user.id
it[name] = user.name
it[domain] = user.domain
it[screenName] = user.screenName
it[description] = user.description
it[password] = user.password
it[inbox] = user.inbox
it[outbox] = user.outbox
it[url] = user.url
it[createdAt] = user.createdAt.toEpochMilli()
it[publicKey] = user.publicKey
it[privateKey] = user.privateKey
}
} else {
Users.update({ Users.id eq user.id }) {
it[name] = user.name
it[domain] = user.domain
it[screenName] = user.screenName
it[description] = user.description
it[password] = user.password
it[inbox] = user.inbox
it[outbox] = user.outbox
it[url] = user.url
it[createdAt] = user.createdAt.toEpochMilli()
it[publicKey] = user.publicKey
it[privateKey] = user.privateKey
}
}
return user
}
override suspend fun findById(id: Long): User? {
return Users.select { Users.id eq id }.map {
it.toUser()
}.singleOrNull()
}
override suspend fun deleteFollowRequest(id: Long, follower: Long) {
FollowRequests.deleteWhere { userId.eq(id) and followerId.eq(follower) }
}
override suspend fun findFollowRequestsById(id: Long, follower: Long): Boolean {
return FollowRequests.select { (FollowRequests.userId eq id) and (FollowRequests.followerId eq follower) }
.singleOrNull() != null
}
override suspend fun delete(id: Long) {
Users.deleteWhere { Users.id.eq(id) }
}
override suspend fun nextId(): Long = idGenerateService.generateId()
}
object Users : Table("users") {
val id = long("id")
val name = varchar("name", length = 64)
val domain = varchar("domain", length = 255)
val screenName = varchar("screen_name", length = 64)
val description = varchar("description", length = 600)
val password = varchar("password", length = 255).nullable()
val inbox = varchar("inbox", length = 255).uniqueIndex()
val outbox = varchar("outbox", length = 255).uniqueIndex()
val url = varchar("url", length = 255).uniqueIndex()
val publicKey = varchar("public_key", length = 10000)
val privateKey = varchar("private_key", length = 10000).nullable()
val createdAt = long("created_at")
override val primaryKey: PrimaryKey = PrimaryKey(id)
init {
uniqueIndex(name, domain)
}
}
fun ResultRow.toUser(): User {
return User(
id = this[Users.id],
name = this[Users.name],
domain = this[Users.domain],
screenName = this[Users.screenName],
description = this[Users.description],
password = this[Users.password],
inbox = this[Users.inbox],
outbox = this[Users.outbox],
url = this[Users.url],
publicKey = this[Users.publicKey],
privateKey = this[Users.privateKey],
createdAt = Instant.ofEpochMilli((this[Users.createdAt]))
)
}
object UsersFollowers : LongIdTable("users_followers") {
val userId = long("user_id").references(Users.id).index()
val followerId = long("follower_id").references(Users.id)
init {
uniqueIndex(userId, followerId)
}
}
object FollowRequests : LongIdTable("follow_requests") {
val userId = long("user_id").references(Users.id)
val followerId = long("follower_id").references(Users.id)
init {
uniqueIndex(userId, followerId)
}
}

View File

@ -1,6 +1,6 @@
package dev.usbharu.hideout.routing package dev.usbharu.hideout.routing
import dev.usbharu.hideout.service.api.IUserApiService import dev.usbharu.hideout.service.api.UserApiService
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.auth.* import io.ktor.server.auth.*
@ -8,7 +8,7 @@ import io.ktor.server.request.*
import io.ktor.server.response.* import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
fun Application.register(userApiService: IUserApiService) { fun Application.register(userApiService: UserApiService) {
routing { routing {
get("/register") { get("/register") {
val principal = call.principal<UserIdPrincipal>() val principal = call.principal<UserIdPrincipal>()

View File

@ -13,7 +13,7 @@ import io.ktor.server.routing.*
fun Routing.inbox( fun Routing.inbox(
httpSignatureVerifyService: HttpSignatureVerifyService, httpSignatureVerifyService: HttpSignatureVerifyService,
activityPubService: dev.usbharu.hideout.service.activitypub.ActivityPubService apService: dev.usbharu.hideout.service.ap.APService
) { ) {
route("/inbox") { route("/inbox") {
get { get {
@ -25,9 +25,9 @@ fun Routing.inbox(
} }
val json = call.receiveText() val json = call.receiveText()
call.application.log.trace("Received: $json") call.application.log.trace("Received: $json")
val activityTypes = activityPubService.parseActivity(json) val activityTypes = apService.parseActivity(json)
call.application.log.debug("ActivityTypes: ${activityTypes.name}") call.application.log.debug("ActivityTypes: ${activityTypes.name}")
val response = activityPubService.processActivity(json, activityTypes) val response = apService.processActivity(json, activityTypes)
when (response) { when (response) {
is ActivityPubObjectResponse -> call.respond( is ActivityPubObjectResponse -> call.respond(
response.httpStatusCode, response.httpStatusCode,
@ -54,9 +54,9 @@ fun Routing.inbox(
} }
val json = call.receiveText() val json = call.receiveText()
call.application.log.trace("Received: $json") call.application.log.trace("Received: $json")
val activityTypes = activityPubService.parseActivity(json) val activityTypes = apService.parseActivity(json)
call.application.log.debug("ActivityTypes: ${activityTypes.name}") call.application.log.debug("ActivityTypes: ${activityTypes.name}")
val response = activityPubService.processActivity(json, activityTypes) val response = apService.processActivity(json, activityTypes)
when (response) { when (response) {
is ActivityPubObjectResponse -> call.respond( is ActivityPubObjectResponse -> call.respond(
response.httpStatusCode, response.httpStatusCode,

View File

@ -5,7 +5,7 @@ import dev.usbharu.hideout.exception.ParameterNotExistException
import dev.usbharu.hideout.plugins.respondAp import dev.usbharu.hideout.plugins.respondAp
import dev.usbharu.hideout.query.FollowerQueryService import dev.usbharu.hideout.query.FollowerQueryService
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService import dev.usbharu.hideout.service.ap.APUserService
import dev.usbharu.hideout.service.core.Transaction import dev.usbharu.hideout.service.core.Transaction
import dev.usbharu.hideout.util.HttpUtil.Activity import dev.usbharu.hideout.util.HttpUtil.Activity
import dev.usbharu.hideout.util.HttpUtil.JsonLd import dev.usbharu.hideout.util.HttpUtil.JsonLd
@ -16,7 +16,7 @@ import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
fun Routing.usersAP( fun Routing.usersAP(
activityPubUserService: ActivityPubUserService, apUserService: APUserService,
userQueryService: UserQueryService, userQueryService: UserQueryService,
followerQueryService: FollowerQueryService, followerQueryService: FollowerQueryService,
transaction: Transaction transaction: Transaction
@ -27,7 +27,7 @@ fun Routing.usersAP(
call.application.log.debug("Authorization: ${call.request.header("Authorization")}") call.application.log.debug("Authorization: ${call.request.header("Authorization")}")
val name = val name =
call.parameters["name"] ?: throw ParameterNotExistException("Parameter(name='name') does not exist.") call.parameters["name"] ?: throw ParameterNotExistException("Parameter(name='name') does not exist.")
val person = activityPubUserService.getPersonByName(name) val person = apUserService.getPersonByName(name)
return@handle call.respondAp( return@handle call.respondAp(
person, person,
HttpStatusCode.OK HttpStatusCode.OK

View File

@ -4,7 +4,7 @@ import dev.usbharu.hideout.domain.model.hideout.form.Post
import dev.usbharu.hideout.domain.model.hideout.form.Reaction import dev.usbharu.hideout.domain.model.hideout.form.Reaction
import dev.usbharu.hideout.exception.ParameterNotExistException import dev.usbharu.hideout.exception.ParameterNotExistException
import dev.usbharu.hideout.plugins.TOKEN_AUTH import dev.usbharu.hideout.plugins.TOKEN_AUTH
import dev.usbharu.hideout.service.api.IPostApiService import dev.usbharu.hideout.service.api.PostApiService
import dev.usbharu.hideout.util.InstantParseUtil import dev.usbharu.hideout.util.InstantParseUtil
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
@ -15,7 +15,7 @@ import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
@Suppress("LongMethod") @Suppress("LongMethod")
fun Route.posts(postApiService: IPostApiService) { fun Route.posts(postApiService: PostApiService) {
route("/posts") { route("/posts") {
authenticate(TOKEN_AUTH) { authenticate(TOKEN_AUTH) {
post { post {

View File

@ -5,8 +5,8 @@ import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
import dev.usbharu.hideout.domain.model.hideout.form.UserCreate import dev.usbharu.hideout.domain.model.hideout.form.UserCreate
import dev.usbharu.hideout.exception.ParameterNotExistException import dev.usbharu.hideout.exception.ParameterNotExistException
import dev.usbharu.hideout.plugins.TOKEN_AUTH import dev.usbharu.hideout.plugins.TOKEN_AUTH
import dev.usbharu.hideout.service.api.IUserApiService import dev.usbharu.hideout.service.api.UserApiService
import dev.usbharu.hideout.service.user.IUserService import dev.usbharu.hideout.service.user.UserService
import dev.usbharu.hideout.util.AcctUtil import dev.usbharu.hideout.util.AcctUtil
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
@ -17,7 +17,7 @@ import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
@Suppress("LongMethod", "CognitiveComplexMethod") @Suppress("LongMethod", "CognitiveComplexMethod")
fun Route.users(userService: IUserService, userApiService: IUserApiService) { fun Route.users(userService: UserService, userApiService: UserApiService) {
route("/users") { route("/users") {
get { get {
call.respond(userApiService.findAll()) call.respond(userApiService.findAll())

View File

@ -1,8 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ap.Accept
interface ActivityPubAcceptService {
suspend fun receiveAccept(accept: Accept): ActivityPubResponse
}

View File

@ -1,8 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ap.Create
interface ActivityPubCreateService {
suspend fun receiveCreate(create: Create): ActivityPubResponse
}

View File

@ -1,8 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ap.Like
interface ActivityPubLikeService {
suspend fun receiveLike(like: Like): ActivityPubResponse
}

View File

@ -1,15 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import dev.usbharu.hideout.domain.model.ap.Note
import dev.usbharu.hideout.domain.model.hideout.entity.Post
import dev.usbharu.hideout.domain.model.job.DeliverPostJob
import kjob.core.job.JobProps
interface ActivityPubNoteService {
suspend fun createNote(post: Post)
suspend fun createNoteJob(props: JobProps<DeliverPostJob>)
suspend fun fetchNote(url: String, targetActor: String? = null): Note
suspend fun fetchNote(note: Note, targetActor: String? = null): Note
}

View File

@ -1,13 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import dev.usbharu.hideout.domain.model.hideout.entity.Reaction
import dev.usbharu.hideout.domain.model.job.DeliverReactionJob
import dev.usbharu.hideout.domain.model.job.DeliverRemoveReactionJob
import kjob.core.job.JobProps
interface ActivityPubReactionService {
suspend fun reaction(like: Reaction)
suspend fun removeReaction(like: Reaction)
suspend fun reactionJob(props: JobProps<DeliverReactionJob>)
suspend fun removeReactionJob(props: JobProps<DeliverRemoveReactionJob>)
}

View File

@ -1,11 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ap.Follow
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
import kjob.core.job.JobProps
interface ActivityPubReceiveFollowService {
suspend fun receiveFollow(follow: Follow): ActivityPubResponse
suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>)
}

View File

@ -1,7 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import dev.usbharu.hideout.domain.model.hideout.dto.SendFollowDto
interface ActivityPubSendFollowService {
suspend fun sendFollow(sendFollowDto: SendFollowDto)
}

View File

@ -1,164 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.job.HideoutJob
import kjob.core.dsl.JobContextWithProps
interface ActivityPubService {
fun parseActivity(json: String): ActivityType
suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse?
suspend fun <T : HideoutJob> processActivity(job: JobContextWithProps<T>, hideoutJob: HideoutJob)
}
enum class ActivityType {
Accept,
Add,
Announce,
Arrive,
Block,
Create,
Delete,
Dislike,
Flag,
Follow,
Ignore,
Invite,
Join,
Leave,
Like,
Listen,
Move,
Offer,
Question,
Reject,
Read,
Remove,
TentativeReject,
TentativeAccept,
Travel,
Undo,
Update,
View,
Other
}
enum class ActivityVocabulary {
Object,
Link,
Activity,
IntransitiveActivity,
Collection,
OrderedCollection,
CollectionPage,
OrderedCollectionPage,
Accept,
Add,
Announce,
Arrive,
Block,
Create,
Delete,
Dislike,
Flag,
Follow,
Ignore,
Invite,
Join,
Leave,
Like,
Listen,
Move,
Offer,
Question,
Reject,
Read,
Remove,
TentativeReject,
TentativeAccept,
Travel,
Undo,
Update,
View,
Application,
Group,
Organization,
Person,
Service,
Article,
Audio,
Document,
Event,
Image,
Note,
Page,
Place,
Profile,
Relationship,
Tombstone,
Video,
Mention,
}
enum class ExtendedActivityVocabulary {
Object,
Link,
Activity,
IntransitiveActivity,
Collection,
OrderedCollection,
CollectionPage,
OrderedCollectionPage,
Accept,
Add,
Announce,
Arrive,
Block,
Create,
Delete,
Dislike,
Flag,
Follow,
Ignore,
Invite,
Join,
Leave,
Like,
Listen,
Move,
Offer,
Question,
Reject,
Read,
Remove,
TentativeReject,
TentativeAccept,
Travel,
Undo,
Update,
View,
Application,
Group,
Organization,
Person,
Service,
Article,
Audio,
Document,
Event,
Image,
Note,
Page,
Place,
Profile,
Relationship,
Tombstone,
Video,
Mention,
Emoji
}
enum class ExtendedVocabulary {
Emoji
}

View File

@ -1,79 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.config.Config.configData
import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ap.Follow
import dev.usbharu.hideout.domain.model.job.*
import dev.usbharu.hideout.exception.JsonParseException
import kjob.core.dsl.JobContextWithProps
import kjob.core.job.JobProps
import org.koin.core.annotation.Single
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@Single
class ActivityPubServiceImpl(
private val activityPubReceiveFollowService: ActivityPubReceiveFollowService,
private val activityPubNoteService: ActivityPubNoteService,
private val activityPubUndoService: ActivityPubUndoService,
private val activityPubAcceptService: ActivityPubAcceptService,
private val activityPubCreateService: ActivityPubCreateService,
private val activityPubLikeService: ActivityPubLikeService,
private val activityPubReactionService: ActivityPubReactionService
) : ActivityPubService {
val logger: Logger = LoggerFactory.getLogger(this::class.java)
override fun parseActivity(json: String): ActivityType {
val readTree = configData.objectMapper.readTree(json)
logger.trace("readTree: {}", readTree)
if (readTree.isObject.not()) {
throw JsonParseException("Json is not object.")
}
val type = readTree["type"]
if (type.isArray) {
return type.firstNotNullOf { jsonNode: JsonNode ->
ActivityType.values().firstOrNull { it.name.equals(jsonNode.asText(), true) }
}
}
return ActivityType.values().first { it.name.equals(type.asText(), true) }
}
@Suppress("CyclomaticComplexMethod", "NotImplementedDeclaration")
override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse {
logger.debug("proccess activity: {}", type)
return when (type) {
ActivityType.Accept -> activityPubAcceptService.receiveAccept(configData.objectMapper.readValue(json))
ActivityType.Follow -> activityPubReceiveFollowService.receiveFollow(
configData.objectMapper.readValue(
json,
Follow::class.java
)
)
ActivityType.Create -> activityPubCreateService.receiveCreate(configData.objectMapper.readValue(json))
ActivityType.Like -> activityPubLikeService.receiveLike(configData.objectMapper.readValue(json))
ActivityType.Undo -> activityPubUndoService.receiveUndo(configData.objectMapper.readValue(json))
else -> {
throw IllegalArgumentException("$type is not supported.")
}
}
}
override suspend fun <T : HideoutJob> processActivity(job: JobContextWithProps<T>, hideoutJob: HideoutJob) {
logger.debug("processActivity: ${hideoutJob.name}")
when (hideoutJob) {
ReceiveFollowJob -> activityPubReceiveFollowService.receiveFollowJob(
job.props as JobProps<ReceiveFollowJob>
)
DeliverPostJob -> activityPubNoteService.createNoteJob(job.props as JobProps<DeliverPostJob>)
DeliverReactionJob -> activityPubReactionService.reactionJob(job.props as JobProps<DeliverReactionJob>)
DeliverRemoveReactionJob -> activityPubReactionService.removeReactionJob(
job.props as JobProps<DeliverRemoveReactionJob>
)
}
}
}

View File

@ -1,8 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ap.Undo
interface ActivityPubUndoService {
suspend fun receiveUndo(undo: Undo): ActivityPubResponse
}

View File

@ -1,16 +0,0 @@
package dev.usbharu.hideout.service.activitypub
import dev.usbharu.hideout.domain.model.ap.Person
interface ActivityPubUserService {
suspend fun getPersonByName(name: String): Person
/**
* Fetch person
*
* @param url
* @param targetActor 署名するユーザー
* @return
*/
suspend fun fetchPerson(url: String, targetActor: String? = null): Person
}

View File

@ -1,4 +1,4 @@
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import dev.usbharu.hideout.domain.model.ActivityPubResponse import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ActivityPubStringResponse import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
@ -6,15 +6,19 @@ 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.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.user.IUserService 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
interface APAcceptService {
suspend fun receiveAccept(accept: Accept): ActivityPubResponse
}
@Single @Single
class ActivityPubAcceptServiceImpl( class APAcceptServiceImpl(
private val userService: IUserService, private val userService: UserService,
private val userQueryService: UserQueryService private val userQueryService: UserQueryService
) : ActivityPubAcceptService { ) : APAcceptService {
override suspend fun receiveAccept(accept: Accept): ActivityPubResponse { override suspend fun receiveAccept(accept: Accept): ActivityPubResponse {
val value = accept.`object` ?: throw IllegalActivityPubObjectException("object is null") val value = accept.`object` ?: throw IllegalActivityPubObjectException("object is null")
if (value.type.contains("Follow").not()) { if (value.type.contains("Follow").not()) {

View File

@ -1,4 +1,4 @@
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import dev.usbharu.hideout.domain.model.ActivityPubResponse import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ActivityPubStringResponse import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
@ -9,11 +9,15 @@ import dev.usbharu.hideout.service.core.Transaction
import io.ktor.http.* import io.ktor.http.*
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
interface APCreateService {
suspend fun receiveCreate(create: Create): ActivityPubResponse
}
@Single @Single
class ActivityPubCreateServiceImpl( class APCreateServiceImpl(
private val activityPubNoteService: ActivityPubNoteService, private val apNoteService: APNoteService,
private val transaction: Transaction private val transaction: Transaction
) : ActivityPubCreateService { ) : APCreateService {
override suspend fun receiveCreate(create: Create): ActivityPubResponse { override suspend fun receiveCreate(create: Create): ActivityPubResponse {
val value = create.`object` ?: throw IllegalActivityPubObjectException("object is null") val value = create.`object` ?: throw IllegalActivityPubObjectException("object is null")
if (value.type.contains("Note").not()) { if (value.type.contains("Note").not()) {
@ -22,7 +26,7 @@ class ActivityPubCreateServiceImpl(
return transaction.transaction { return transaction.transaction {
val note = value as Note val note = value as Note
activityPubNoteService.fetchNote(note) apNoteService.fetchNote(note)
ActivityPubStringResponse(HttpStatusCode.OK, "Created") ActivityPubStringResponse(HttpStatusCode.OK, "Created")
} }
} }

View File

@ -1,4 +1,4 @@
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import dev.usbharu.hideout.domain.model.ActivityPubResponse import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ActivityPubStringResponse import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
@ -7,26 +7,30 @@ import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException
import dev.usbharu.hideout.query.PostQueryService import dev.usbharu.hideout.query.PostQueryService
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.core.Transaction import dev.usbharu.hideout.service.core.Transaction
import dev.usbharu.hideout.service.reaction.IReactionService import dev.usbharu.hideout.service.reaction.ReactionService
import io.ktor.http.* import io.ktor.http.*
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
interface APLikeService {
suspend fun receiveLike(like: Like): ActivityPubResponse
}
@Single @Single
class ActivityPubLikeServiceImpl( class APLikeServiceImpl(
private val reactionService: IReactionService, private val reactionService: ReactionService,
private val activityPubUserService: ActivityPubUserService, private val apUserService: APUserService,
private val activityPubNoteService: ActivityPubNoteService, private val apNoteService: APNoteService,
private val userQueryService: UserQueryService, private val userQueryService: UserQueryService,
private val postQueryService: PostQueryService, private val postQueryService: PostQueryService,
private val transaction: Transaction private val transaction: Transaction
) : ActivityPubLikeService { ) : APLikeService {
override suspend fun receiveLike(like: Like): ActivityPubResponse { override suspend fun receiveLike(like: Like): ActivityPubResponse {
val actor = like.actor ?: throw IllegalActivityPubObjectException("actor is null") val actor = like.actor ?: throw IllegalActivityPubObjectException("actor is null")
val content = like.content ?: throw IllegalActivityPubObjectException("content is null") val content = like.content ?: throw IllegalActivityPubObjectException("content is null")
like.`object` ?: throw IllegalActivityPubObjectException("object is null") like.`object` ?: throw IllegalActivityPubObjectException("object is null")
transaction.transaction { transaction.transaction {
val person = activityPubUserService.fetchPerson(actor) val person = apUserService.fetchPerson(actor)
activityPubNoteService.fetchNote(like.`object`!!) apNoteService.fetchNote(like.`object`!!)
val user = userQueryService.findByUrl( val user = userQueryService.findByUrl(
person.url person.url

View File

@ -1,4 +1,4 @@
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.config.Config import dev.usbharu.hideout.config.Config
@ -13,7 +13,7 @@ import dev.usbharu.hideout.plugins.postAp
import dev.usbharu.hideout.query.FollowerQueryService import dev.usbharu.hideout.query.FollowerQueryService
import dev.usbharu.hideout.query.PostQueryService import dev.usbharu.hideout.query.PostQueryService
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.repository.IPostRepository import dev.usbharu.hideout.repository.PostRepository
import dev.usbharu.hideout.service.job.JobQueueParentService import dev.usbharu.hideout.service.job.JobQueueParentService
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.statement.* import io.ktor.client.statement.*
@ -22,16 +22,25 @@ import org.koin.core.annotation.Single
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.time.Instant import java.time.Instant
interface APNoteService {
suspend fun createNote(post: Post)
suspend fun createNoteJob(props: JobProps<DeliverPostJob>)
suspend fun fetchNote(url: String, targetActor: String? = null): Note
suspend fun fetchNote(note: Note, targetActor: String? = null): Note
}
@Single @Single
class ActivityPubNoteServiceImpl( class APNoteServiceImpl(
private val httpClient: HttpClient, private val httpClient: HttpClient,
private val jobQueueParentService: JobQueueParentService, private val jobQueueParentService: JobQueueParentService,
private val postRepository: IPostRepository, private val postRepository: PostRepository,
private val activityPubUserService: ActivityPubUserService, private val apUserService: APUserService,
private val userQueryService: UserQueryService, private val userQueryService: UserQueryService,
private val followerQueryService: FollowerQueryService, private val followerQueryService: FollowerQueryService,
private val postQueryService: PostQueryService private val postQueryService: PostQueryService
) : ActivityPubNoteService { ) : APNoteService {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
@ -41,9 +50,9 @@ class ActivityPubNoteServiceImpl(
val note = Config.configData.objectMapper.writeValueAsString(post) val note = Config.configData.objectMapper.writeValueAsString(post)
followers.forEach { followerEntity -> followers.forEach { followerEntity ->
jobQueueParentService.schedule(DeliverPostJob) { jobQueueParentService.schedule(DeliverPostJob) {
props[it.actor] = userEntity.url props[DeliverPostJob.actor] = userEntity.url
props[it.post] = note props[DeliverPostJob.post] = note
props[it.inbox] = followerEntity.inbox props[DeliverPostJob.inbox] = followerEntity.inbox
} }
} }
} }
@ -75,9 +84,12 @@ class ActivityPubNoteServiceImpl(
override suspend fun fetchNote(url: String, targetActor: String?): Note { override suspend fun fetchNote(url: String, targetActor: String?): Note {
val post = postQueryService.findByUrl(url) val post = postQueryService.findByUrl(url)
if (post != null) { try {
return postToNote(post) return postToNote(post)
} catch (_: NoSuchElementException) {
} catch (_: IllegalArgumentException) {
} }
val response = httpClient.getAp( val response = httpClient.getAp(
url, url,
targetActor?.let { "$targetActor#pubkey" } targetActor?.let { "$targetActor#pubkey" }
@ -118,7 +130,7 @@ class ActivityPubNoteServiceImpl(
} }
private suspend fun internalNote(note: Note, targetActor: String?, url: String): Note { private suspend fun internalNote(note: Note, targetActor: String?, url: String): Note {
val person = activityPubUserService.fetchPerson( val person = apUserService.fetchPerson(
note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"), note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"),
targetActor targetActor
) )

View File

@ -1,4 +1,4 @@
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.config.Config import dev.usbharu.hideout.config.Config
@ -11,22 +11,29 @@ import dev.usbharu.hideout.plugins.postAp
import dev.usbharu.hideout.query.FollowerQueryService import dev.usbharu.hideout.query.FollowerQueryService
import dev.usbharu.hideout.query.PostQueryService import dev.usbharu.hideout.query.PostQueryService
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.repository.IPostRepository import dev.usbharu.hideout.repository.PostRepository
import dev.usbharu.hideout.service.job.JobQueueParentService import dev.usbharu.hideout.service.job.JobQueueParentService
import io.ktor.client.* import io.ktor.client.*
import kjob.core.job.JobProps import kjob.core.job.JobProps
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
import java.time.Instant import java.time.Instant
interface APReactionService {
suspend fun reaction(like: Reaction)
suspend fun removeReaction(like: Reaction)
suspend fun reactionJob(props: JobProps<DeliverReactionJob>)
suspend fun removeReactionJob(props: JobProps<DeliverRemoveReactionJob>)
}
@Single @Single
class ActivityPubReactionServiceImpl( class APReactionServiceImpl(
private val jobQueueParentService: JobQueueParentService, private val jobQueueParentService: JobQueueParentService,
private val iPostRepository: IPostRepository, private val postRepository: PostRepository,
private val httpClient: HttpClient, private val httpClient: HttpClient,
private val userQueryService: UserQueryService, private val userQueryService: UserQueryService,
private val followerQueryService: FollowerQueryService, private val followerQueryService: FollowerQueryService,
private val postQueryService: PostQueryService private val postQueryService: PostQueryService
) : ActivityPubReactionService { ) : APReactionService {
override suspend fun reaction(like: Reaction) { override suspend fun reaction(like: Reaction) {
val followers = followerQueryService.findFollowersById(like.userId) val followers = followerQueryService.findFollowersById(like.userId)
val user = userQueryService.findById(like.userId) val user = userQueryService.findById(like.userId)
@ -34,11 +41,11 @@ class ActivityPubReactionServiceImpl(
postQueryService.findById(like.postId) postQueryService.findById(like.postId)
followers.forEach { follower -> followers.forEach { follower ->
jobQueueParentService.schedule(DeliverReactionJob) { jobQueueParentService.schedule(DeliverReactionJob) {
props[it.actor] = user.url props[DeliverReactionJob.actor] = user.url
props[it.reaction] = "" props[DeliverReactionJob.reaction] = ""
props[it.inbox] = follower.inbox props[DeliverReactionJob.inbox] = follower.inbox
props[it.postUrl] = post.url props[DeliverReactionJob.postUrl] = post.url
props[it.id] = post.id.toString() props[DeliverReactionJob.id] = post.id.toString()
} }
} }
} }
@ -50,10 +57,10 @@ class ActivityPubReactionServiceImpl(
postQueryService.findById(like.postId) postQueryService.findById(like.postId)
followers.forEach { follower -> followers.forEach { follower ->
jobQueueParentService.schedule(DeliverRemoveReactionJob) { jobQueueParentService.schedule(DeliverRemoveReactionJob) {
props[it.actor] = user.url props[DeliverRemoveReactionJob.actor] = user.url
props[it.inbox] = follower.inbox props[DeliverRemoveReactionJob.inbox] = follower.inbox
props[it.id] = post.id.toString() props[DeliverRemoveReactionJob.id] = post.id.toString()
props[it.like] = Config.configData.objectMapper.writeValueAsString(like) props[DeliverRemoveReactionJob.like] = Config.configData.objectMapper.writeValueAsString(like)
} }
} }
} }

View File

@ -1,4 +1,4 @@
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.config.Config import dev.usbharu.hideout.config.Config
@ -11,27 +11,32 @@ import dev.usbharu.hideout.plugins.postAp
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.core.Transaction import dev.usbharu.hideout.service.core.Transaction
import dev.usbharu.hideout.service.job.JobQueueParentService import dev.usbharu.hideout.service.job.JobQueueParentService
import dev.usbharu.hideout.service.user.IUserService import dev.usbharu.hideout.service.user.UserService
import io.ktor.client.* import io.ktor.client.*
import io.ktor.http.* import io.ktor.http.*
import kjob.core.job.JobProps import kjob.core.job.JobProps
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
interface APReceiveFollowService {
suspend fun receiveFollow(follow: Follow): ActivityPubResponse
suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>)
}
@Single @Single
class ActivityPubReceiveFollowServiceImpl( class APReceiveFollowServiceImpl(
private val jobQueueParentService: JobQueueParentService, private val jobQueueParentService: JobQueueParentService,
private val activityPubUserService: ActivityPubUserService, private val apUserService: APUserService,
private val userService: IUserService, private val userService: UserService,
private val httpClient: HttpClient, private val httpClient: HttpClient,
private val userQueryService: UserQueryService, private val userQueryService: UserQueryService,
private val transaction: Transaction private val transaction: Transaction
) : ActivityPubReceiveFollowService { ) : APReceiveFollowService {
override suspend fun receiveFollow(follow: Follow): ActivityPubResponse { override suspend fun receiveFollow(follow: Follow): ActivityPubResponse {
// TODO: Verify HTTP Signature // TODO: Verify HTTP Signature
jobQueueParentService.schedule(ReceiveFollowJob) { jobQueueParentService.schedule(ReceiveFollowJob) {
props[it.actor] = follow.actor props[ReceiveFollowJob.actor] = follow.actor
props[it.follow] = Config.configData.objectMapper.writeValueAsString(follow) props[ReceiveFollowJob.follow] = Config.configData.objectMapper.writeValueAsString(follow)
props[it.targetActor] = follow.`object` props[ReceiveFollowJob.targetActor] = follow.`object`
} }
return ActivityPubStringResponse(HttpStatusCode.OK, "{}", ContentType.Application.Json) return ActivityPubStringResponse(HttpStatusCode.OK, "{}", ContentType.Application.Json)
} }
@ -40,7 +45,7 @@ class ActivityPubReceiveFollowServiceImpl(
transaction.transaction { transaction.transaction {
val actor = props[ReceiveFollowJob.actor] val actor = props[ReceiveFollowJob.actor]
val targetActor = props[ReceiveFollowJob.targetActor] val targetActor = props[ReceiveFollowJob.targetActor]
val person = activityPubUserService.fetchPerson(actor, targetActor) val person = apUserService.fetchPerson(actor, targetActor)
val follow = Config.configData.objectMapper.readValue<Follow>(props[ReceiveFollowJob.follow]) val follow = Config.configData.objectMapper.readValue<Follow>(props[ReceiveFollowJob.follow])
httpClient.postAp( httpClient.postAp(
urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found"), urlString = person.inbox ?: throw IllegalArgumentException("inbox is not found"),

View File

@ -1,4 +1,4 @@
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import dev.usbharu.hideout.domain.model.ap.Follow import dev.usbharu.hideout.domain.model.ap.Follow
import dev.usbharu.hideout.domain.model.hideout.dto.SendFollowDto import dev.usbharu.hideout.domain.model.hideout.dto.SendFollowDto
@ -6,8 +6,12 @@ import dev.usbharu.hideout.plugins.postAp
import io.ktor.client.* import io.ktor.client.*
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
interface APSendFollowService {
suspend fun sendFollow(sendFollowDto: SendFollowDto)
}
@Single @Single
class ActivityPubSendFollowServiceImpl(private val httpClient: HttpClient) : ActivityPubSendFollowService { class APSendFollowServiceImpl(private val httpClient: HttpClient) : APSendFollowService {
override suspend fun sendFollow(sendFollowDto: SendFollowDto) { override suspend fun sendFollow(sendFollowDto: SendFollowDto) {
val follow = Follow( val follow = Follow(
name = "Follow", name = "Follow",

View File

@ -0,0 +1,238 @@
package dev.usbharu.hideout.service.ap
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ap.Follow
import dev.usbharu.hideout.domain.model.job.*
import dev.usbharu.hideout.exception.JsonParseException
import kjob.core.dsl.JobContextWithProps
import kjob.core.job.JobProps
import org.koin.core.annotation.Single
import org.slf4j.Logger
import org.slf4j.LoggerFactory
interface APService {
fun parseActivity(json: String): ActivityType
suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse?
suspend fun <T : HideoutJob> processActivity(job: JobContextWithProps<T>, hideoutJob: HideoutJob)
}
enum class ActivityType {
Accept,
Add,
Announce,
Arrive,
Block,
Create,
Delete,
Dislike,
Flag,
Follow,
Ignore,
Invite,
Join,
Leave,
Like,
Listen,
Move,
Offer,
Question,
Reject,
Read,
Remove,
TentativeReject,
TentativeAccept,
Travel,
Undo,
Update,
View,
Other
}
enum class ActivityVocabulary {
Object,
Link,
Activity,
IntransitiveActivity,
Collection,
OrderedCollection,
CollectionPage,
OrderedCollectionPage,
Accept,
Add,
Announce,
Arrive,
Block,
Create,
Delete,
Dislike,
Flag,
Follow,
Ignore,
Invite,
Join,
Leave,
Like,
Listen,
Move,
Offer,
Question,
Reject,
Read,
Remove,
TentativeReject,
TentativeAccept,
Travel,
Undo,
Update,
View,
Application,
Group,
Organization,
Person,
Service,
Article,
Audio,
Document,
Event,
Image,
Note,
Page,
Place,
Profile,
Relationship,
Tombstone,
Video,
Mention,
}
enum class ExtendedActivityVocabulary {
Object,
Link,
Activity,
IntransitiveActivity,
Collection,
OrderedCollection,
CollectionPage,
OrderedCollectionPage,
Accept,
Add,
Announce,
Arrive,
Block,
Create,
Delete,
Dislike,
Flag,
Follow,
Ignore,
Invite,
Join,
Leave,
Like,
Listen,
Move,
Offer,
Question,
Reject,
Read,
Remove,
TentativeReject,
TentativeAccept,
Travel,
Undo,
Update,
View,
Application,
Group,
Organization,
Person,
Service,
Article,
Audio,
Document,
Event,
Image,
Note,
Page,
Place,
Profile,
Relationship,
Tombstone,
Video,
Mention,
Emoji
}
enum class ExtendedVocabulary {
Emoji
}
@Single
class APServiceImpl(
private val apReceiveFollowService: APReceiveFollowService,
private val apNoteService: APNoteService,
private val apUndoService: APUndoService,
private val apAcceptService: APAcceptService,
private val apCreateService: APCreateService,
private val apLikeService: APLikeService,
private val apReactionService: APReactionService
) : APService {
val logger: Logger = LoggerFactory.getLogger(this::class.java)
override fun parseActivity(json: String): ActivityType {
val readTree = Config.configData.objectMapper.readTree(json)
logger.trace("readTree: {}", readTree)
if (readTree.isObject.not()) {
throw JsonParseException("Json is not object.")
}
val type = readTree["type"]
if (type.isArray) {
return type.firstNotNullOf { jsonNode: JsonNode ->
ActivityType.values().firstOrNull { it.name.equals(jsonNode.asText(), true) }
}
}
return ActivityType.values().first { it.name.equals(type.asText(), true) }
}
@Suppress("CyclomaticComplexMethod", "NotImplementedDeclaration")
override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse {
logger.debug("proccess activity: {}", type)
return when (type) {
ActivityType.Accept -> apAcceptService.receiveAccept(Config.configData.objectMapper.readValue(json))
ActivityType.Follow -> apReceiveFollowService.receiveFollow(
Config.configData.objectMapper.readValue(
json,
Follow::class.java
)
)
ActivityType.Create -> apCreateService.receiveCreate(Config.configData.objectMapper.readValue(json))
ActivityType.Like -> apLikeService.receiveLike(Config.configData.objectMapper.readValue(json))
ActivityType.Undo -> apUndoService.receiveUndo(Config.configData.objectMapper.readValue(json))
else -> {
throw IllegalArgumentException("$type is not supported.")
}
}
}
override suspend fun <T : HideoutJob> processActivity(job: JobContextWithProps<T>, hideoutJob: HideoutJob) {
logger.debug("processActivity: ${hideoutJob.name}")
when (hideoutJob) {
ReceiveFollowJob -> apReceiveFollowService.receiveFollowJob(
job.props as JobProps<ReceiveFollowJob>
)
DeliverPostJob -> apNoteService.createNoteJob(job.props as JobProps<DeliverPostJob>)
DeliverReactionJob -> apReactionService.reactionJob(job.props as JobProps<DeliverReactionJob>)
DeliverRemoveReactionJob -> apReactionService.removeReactionJob(
job.props as JobProps<DeliverRemoveReactionJob>
)
}
}
}

View File

@ -1,4 +1,4 @@
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import dev.usbharu.hideout.domain.model.ActivityPubResponse import dev.usbharu.hideout.domain.model.ActivityPubResponse
import dev.usbharu.hideout.domain.model.ActivityPubStringResponse import dev.usbharu.hideout.domain.model.ActivityPubStringResponse
@ -6,18 +6,22 @@ import dev.usbharu.hideout.domain.model.ap.Follow
import dev.usbharu.hideout.domain.model.ap.Undo import dev.usbharu.hideout.domain.model.ap.Undo
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.core.Transaction import dev.usbharu.hideout.service.core.Transaction
import dev.usbharu.hideout.service.user.IUserService 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
interface APUndoService {
suspend fun receiveUndo(undo: Undo): ActivityPubResponse
}
@Single @Single
@Suppress("UnsafeCallOnNullableType") @Suppress("UnsafeCallOnNullableType")
class ActivityPubUndoServiceImpl( class APUndoServiceImpl(
private val userService: IUserService, private val userService: UserService,
private val activityPubUserService: ActivityPubUserService, private val apUserService: APUserService,
private val userQueryService: UserQueryService, private val userQueryService: UserQueryService,
private val transaction: Transaction private val transaction: Transaction
) : ActivityPubUndoService { ) : APUndoService {
override suspend fun receiveUndo(undo: Undo): ActivityPubResponse { override suspend fun receiveUndo(undo: Undo): ActivityPubResponse {
if (undo.actor == null) { if (undo.actor == null) {
return ActivityPubStringResponse(HttpStatusCode.BadRequest, "actor is null") return ActivityPubStringResponse(HttpStatusCode.BadRequest, "actor is null")
@ -36,7 +40,7 @@ class ActivityPubUndoServiceImpl(
return ActivityPubStringResponse(HttpStatusCode.BadRequest, "object.object is null") return ActivityPubStringResponse(HttpStatusCode.BadRequest, "object.object is null")
} }
transaction.transaction { transaction.transaction {
activityPubUserService.fetchPerson(undo.actor!!, follow.`object`) apUserService.fetchPerson(undo.actor!!, follow.`object`)
val follower = userQueryService.findByUrl(undo.actor!!) val follower = userQueryService.findByUrl(undo.actor!!)
val target = userQueryService.findByUrl(follow.`object`!!) val target = userQueryService.findByUrl(follow.`object`!!)
userService.unfollow(target.id, follower.id) userService.unfollow(target.id, follower.id)

View File

@ -1,4 +1,4 @@
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.config.Config import dev.usbharu.hideout.config.Config
@ -10,7 +10,7 @@ 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
import dev.usbharu.hideout.service.core.Transaction import dev.usbharu.hideout.service.core.Transaction
import dev.usbharu.hideout.service.user.IUserService import dev.usbharu.hideout.service.user.UserService
import dev.usbharu.hideout.util.HttpUtil.Activity import dev.usbharu.hideout.util.HttpUtil.Activity
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.request.* import io.ktor.client.request.*
@ -18,14 +18,27 @@ import io.ktor.client.statement.*
import io.ktor.http.* import io.ktor.http.*
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
interface APUserService {
suspend fun getPersonByName(name: String): Person
/**
* Fetch person
*
* @param url
* @param targetActor 署名するユーザー
* @return
*/
suspend fun fetchPerson(url: String, targetActor: String? = null): Person
}
@Single @Single
class ActivityPubUserServiceImpl( class APUserServiceImpl(
private val userService: IUserService, private val userService: UserService,
private val httpClient: HttpClient, private val httpClient: HttpClient,
private val userQueryService: UserQueryService, private val userQueryService: UserQueryService,
private val transaction: Transaction private val transaction: Transaction
) : ) :
ActivityPubUserService { APUserService {
override suspend fun getPersonByName(name: String): Person { override suspend fun getPersonByName(name: String): Person {
val userEntity = transaction.transaction { val userEntity = transaction.transaction {

View File

@ -1,33 +0,0 @@
package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse
import dev.usbharu.hideout.domain.model.hideout.dto.ReactionResponse
import java.time.Instant
@Suppress("LongParameterList")
interface IPostApiService {
suspend fun createPost(postForm: dev.usbharu.hideout.domain.model.hideout.form.Post, userId: Long): PostResponse
suspend fun getById(id: Long, userId: Long?): PostResponse
suspend fun getAll(
since: Instant? = null,
until: Instant? = null,
minId: Long? = null,
maxId: Long? = null,
limit: Int? = null,
userId: Long? = null
): List<PostResponse>
suspend fun getByUser(
nameOrId: String,
since: Instant? = null,
until: Instant? = null,
minId: Long? = null,
maxId: Long? = null,
limit: Int? = null,
userId: Long? = null
): List<PostResponse>
suspend fun getReactionByPostId(postId: Long, userId: Long? = null): List<ReactionResponse>
suspend fun appendReaction(reaction: String, userId: Long, postId: Long)
suspend fun removeReaction(userId: Long, postId: Long)
}

View File

@ -1,24 +0,0 @@
package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.domain.model.Acct
import dev.usbharu.hideout.domain.model.hideout.dto.UserResponse
interface IUserApiService {
suspend fun findAll(limit: Int? = 100, offset: Long = 0): List<UserResponse>
suspend fun findById(id: Long): UserResponse
suspend fun findByIds(ids: List<Long>): List<UserResponse>
suspend fun findByAcct(acct: Acct): UserResponse
suspend fun findFollowers(userId: Long): List<UserResponse>
suspend fun findFollowings(userId: Long): List<UserResponse>
suspend fun findFollowersByAcct(acct: Acct): List<UserResponse>
suspend fun findFollowingsByAcct(acct: Acct): List<UserResponse>
suspend fun createUser(username: String, password: String): UserResponse
}

View File

@ -4,27 +4,55 @@ import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse
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.form.Post
import dev.usbharu.hideout.query.PostResponseQueryService import dev.usbharu.hideout.query.PostResponseQueryService
import dev.usbharu.hideout.query.ReactionQueryService import dev.usbharu.hideout.query.ReactionQueryService
import dev.usbharu.hideout.repository.IUserRepository import dev.usbharu.hideout.repository.UserRepository
import dev.usbharu.hideout.service.core.Transaction import dev.usbharu.hideout.service.core.Transaction
import dev.usbharu.hideout.service.post.IPostService import dev.usbharu.hideout.service.post.PostService
import dev.usbharu.hideout.service.reaction.IReactionService import dev.usbharu.hideout.service.reaction.ReactionService
import dev.usbharu.hideout.util.AcctUtil import dev.usbharu.hideout.util.AcctUtil
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
import java.time.Instant import java.time.Instant
import dev.usbharu.hideout.domain.model.hideout.form.Post as FormPost
@Suppress("LongParameterList")
interface PostApiService {
suspend fun createPost(postForm: dev.usbharu.hideout.domain.model.hideout.form.Post, userId: Long): PostResponse
suspend fun getById(id: Long, userId: Long?): PostResponse
suspend fun getAll(
since: Instant? = null,
until: Instant? = null,
minId: Long? = null,
maxId: Long? = null,
limit: Int? = null,
userId: Long? = null
): List<PostResponse>
suspend fun getByUser(
nameOrId: String,
since: Instant? = null,
until: Instant? = null,
minId: Long? = null,
maxId: Long? = null,
limit: Int? = null,
userId: Long? = null
): List<PostResponse>
suspend fun getReactionByPostId(postId: Long, userId: Long? = null): List<ReactionResponse>
suspend fun appendReaction(reaction: String, userId: Long, postId: Long)
suspend fun removeReaction(userId: Long, postId: Long)
}
@Single @Single
class PostApiServiceImpl( class PostApiServiceImpl(
private val postService: IPostService, private val postService: PostService,
private val userRepository: IUserRepository, private val userRepository: UserRepository,
private val postResponseQueryService: PostResponseQueryService, private val postResponseQueryService: PostResponseQueryService,
private val reactionQueryService: ReactionQueryService, private val reactionQueryService: ReactionQueryService,
private val reactionService: IReactionService, private val reactionService: ReactionService,
private val transaction: Transaction private val transaction: Transaction
) : IPostApiService { ) : PostApiService {
override suspend fun createPost(postForm: FormPost, userId: Long): PostResponse { override suspend fun createPost(postForm: Post, userId: Long): PostResponse {
return transaction.transaction { return transaction.transaction {
val createdPost = postService.createLocal( val createdPost = postService.createLocal(
PostCreateDto( PostCreateDto(

View File

@ -8,17 +8,37 @@ import dev.usbharu.hideout.exception.UsernameAlreadyExistException
import dev.usbharu.hideout.query.FollowerQueryService 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.core.Transaction
import dev.usbharu.hideout.service.user.IUserService import dev.usbharu.hideout.service.user.UserService
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
import kotlin.math.min import kotlin.math.min
interface UserApiService {
suspend fun findAll(limit: Int? = 100, offset: Long = 0): List<UserResponse>
suspend fun findById(id: Long): UserResponse
suspend fun findByIds(ids: List<Long>): List<UserResponse>
suspend fun findByAcct(acct: Acct): UserResponse
suspend fun findFollowers(userId: Long): List<UserResponse>
suspend fun findFollowings(userId: Long): List<UserResponse>
suspend fun findFollowersByAcct(acct: Acct): List<UserResponse>
suspend fun findFollowingsByAcct(acct: Acct): List<UserResponse>
suspend fun createUser(username: String, password: String): UserResponse
}
@Single @Single
class UserApiServiceImpl( class UserApiServiceImpl(
private val userQueryService: UserQueryService, private val userQueryService: UserQueryService,
private val followerQueryService: FollowerQueryService, private val followerQueryService: FollowerQueryService,
private val userService: IUserService, private val userService: UserService,
private val transaction: Transaction private val transaction: Transaction
) : IUserApiService { ) : UserApiService {
override suspend fun findAll(limit: Int?, offset: Long): List<UserResponse> = override suspend fun findAll(limit: Int?, offset: Long): List<UserResponse> =
userQueryService.findAll(min(limit ?: 100, 100), offset).map { UserResponse.from(it) } userQueryService.findAll(min(limit ?: 100, 100), offset).map { UserResponse.from(it) }

View File

@ -1,9 +1,40 @@
package dev.usbharu.hideout.service.api package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.dto.JwtToken import dev.usbharu.hideout.domain.model.hideout.dto.JwtToken
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
import dev.usbharu.hideout.exception.InvalidUsernameOrPasswordException
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.auth.JwtService
import dev.usbharu.hideout.service.core.Transaction
import dev.usbharu.hideout.service.user.UserAuthServiceImpl
import org.koin.core.annotation.Single
interface UserAuthApiService { interface UserAuthApiService {
suspend fun login(username: String, password: String): JwtToken suspend fun login(username: String, password: String): JwtToken
suspend fun refreshToken(refreshToken: RefreshToken): JwtToken suspend fun refreshToken(refreshToken: RefreshToken): JwtToken
} }
@Single
class UserAuthApiServiceImpl(
private val userAuthService: UserAuthServiceImpl,
private val userQueryService: UserQueryService,
private val jwtService: JwtService,
private val transaction: Transaction
) : UserAuthApiService {
override suspend fun login(username: String, password: String): JwtToken {
return transaction.transaction {
if (userAuthService.verifyAccount(username, password).not()) {
throw InvalidUsernameOrPasswordException()
}
val user = userQueryService.findByNameAndDomain(username, Config.configData.domain)
jwtService.createToken(user)
}
}
override suspend fun refreshToken(refreshToken: RefreshToken): JwtToken {
return transaction.transaction {
jwtService.refreshToken(refreshToken)
}
}
}

View File

@ -1,35 +0,0 @@
package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.dto.JwtToken
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
import dev.usbharu.hideout.exception.InvalidUsernameOrPasswordException
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.auth.IJwtService
import dev.usbharu.hideout.service.core.Transaction
import dev.usbharu.hideout.service.user.UserAuthService
import org.koin.core.annotation.Single
@Single
class UserAuthApiServiceImpl(
private val userAuthService: UserAuthService,
private val userQueryService: UserQueryService,
private val jwtService: IJwtService,
private val transaction: Transaction
) : UserAuthApiService {
override suspend fun login(username: String, password: String): JwtToken {
return transaction.transaction {
if (userAuthService.verifyAccount(username, password).not()) {
throw InvalidUsernameOrPasswordException()
}
val user = userQueryService.findByNameAndDomain(username, Config.configData.domain)
jwtService.createToken(user)
}
}
override suspend fun refreshToken(refreshToken: RefreshToken): JwtToken {
return transaction.transaction {
jwtService.refreshToken(refreshToken)
}
}
}

View File

@ -1,7 +1,20 @@
package dev.usbharu.hideout.service.api package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.core.Transaction
import org.koin.core.annotation.Single
interface WebFingerApiService { interface WebFingerApiService {
suspend fun findByNameAndDomain(name: String, domain: String): User suspend fun findByNameAndDomain(name: String, domain: String): User
} }
@Single
class WebFingerApiServiceImpl(private val transaction: Transaction, private val userQueryService: UserQueryService) :
WebFingerApiService {
override suspend fun findByNameAndDomain(name: String, domain: String): User {
return transaction.transaction {
userQueryService.findByNameAndDomain(name, domain)
}
}
}

View File

@ -1,16 +0,0 @@
package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.core.Transaction
import org.koin.core.annotation.Single
@Single
class WebFingerApiServiceImpl(private val transaction: Transaction, private val userQueryService: UserQueryService) :
WebFingerApiService {
override suspend fun findByNameAndDomain(name: String, domain: String): User {
return transaction.transaction {
userQueryService.findByNameAndDomain(name, domain)
}
}
}

View File

@ -1,7 +1,33 @@
package dev.usbharu.hideout.service.auth package dev.usbharu.hideout.service.auth
import dev.usbharu.hideout.plugins.KtorKeyMap
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.core.Transaction
import io.ktor.http.* import io.ktor.http.*
import org.koin.core.annotation.Single
import tech.barbero.http.message.signing.SignatureHeaderVerifier
interface HttpSignatureVerifyService { interface HttpSignatureVerifyService {
fun verify(headers: Headers): Boolean fun verify(headers: Headers): Boolean
} }
@Single
class HttpSignatureVerifyServiceImpl(
private val userQueryService: UserQueryService,
private val transaction: Transaction
) : HttpSignatureVerifyService {
override fun verify(headers: Headers): Boolean {
val build = SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userQueryService, transaction)).build()
return true
// build.verify(object : HttpMessage {
// override fun headerValues(name: String?): MutableList<String> {
// return name?.let { headers.getAll(it) }?.toMutableList() ?: mutableListOf()
// }
//
// override fun addHeader(name: String?, value: String?) {
// TODO()
// }
//
// })
}
}

View File

@ -1,29 +0,0 @@
package dev.usbharu.hideout.service.auth
import dev.usbharu.hideout.plugins.KtorKeyMap
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.core.Transaction
import io.ktor.http.*
import org.koin.core.annotation.Single
import tech.barbero.http.message.signing.SignatureHeaderVerifier
@Single
class HttpSignatureVerifyServiceImpl(
private val userQueryService: UserQueryService,
private val transaction: Transaction
) : HttpSignatureVerifyService {
override fun verify(headers: Headers): Boolean {
val build = SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userQueryService, transaction)).build()
return true
// build.verify(object : HttpMessage {
// override fun headerValues(name: String?): MutableList<String> {
// return name?.let { headers.getAll(it) }?.toMutableList() ?: mutableListOf()
// }
//
// override fun addHeader(name: String?, value: String?) {
// TODO()
// }
//
// })
}
}

View File

@ -1,14 +0,0 @@
package dev.usbharu.hideout.service.auth
import dev.usbharu.hideout.domain.model.hideout.dto.JwtToken
import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
interface IJwtService {
suspend fun createToken(user: User): JwtToken
suspend fun refreshToken(refreshToken: RefreshToken): JwtToken
suspend fun revokeToken(refreshToken: RefreshToken)
suspend fun revokeToken(user: User)
suspend fun revokeAll()
}

View File

@ -10,8 +10,8 @@ import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
import dev.usbharu.hideout.exception.InvalidRefreshTokenException import dev.usbharu.hideout.exception.InvalidRefreshTokenException
import dev.usbharu.hideout.query.JwtRefreshTokenQueryService import dev.usbharu.hideout.query.JwtRefreshTokenQueryService
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.repository.IJwtRefreshTokenRepository import dev.usbharu.hideout.repository.JwtRefreshTokenRepository
import dev.usbharu.hideout.service.core.IMetaService import dev.usbharu.hideout.service.core.MetaService
import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.hideout.util.RsaUtil
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
@ -19,14 +19,23 @@ import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.* import java.util.*
interface JwtService {
suspend fun createToken(user: User): JwtToken
suspend fun refreshToken(refreshToken: RefreshToken): JwtToken
suspend fun revokeToken(refreshToken: RefreshToken)
suspend fun revokeToken(user: User)
suspend fun revokeAll()
}
@Suppress("InjectDispatcher") @Suppress("InjectDispatcher")
@Single @Single
class JwtServiceImpl( class JwtServiceImpl(
private val metaService: IMetaService, private val metaService: MetaService,
private val refreshTokenRepository: IJwtRefreshTokenRepository, private val refreshTokenRepository: JwtRefreshTokenRepository,
private val userQueryService: UserQueryService, private val userQueryService: UserQueryService,
private val refreshTokenQueryService: JwtRefreshTokenQueryService private val refreshTokenQueryService: JwtRefreshTokenQueryService
) : IJwtService { ) : JwtService {
private val privateKey = runBlocking { private val privateKey = runBlocking {
RsaUtil.decodeRsaPrivateKey(metaService.getJwtMeta().privateKey) RsaUtil.decodeRsaPrivateKey(metaService.getJwtMeta().privateKey)

View File

@ -3,7 +3,7 @@ package dev.usbharu.hideout.service.core
import dev.usbharu.hideout.domain.model.hideout.entity.Jwt import dev.usbharu.hideout.domain.model.hideout.entity.Jwt
import dev.usbharu.hideout.domain.model.hideout.entity.Meta import dev.usbharu.hideout.domain.model.hideout.entity.Meta
interface IMetaService { interface MetaService {
suspend fun getMeta(): Meta suspend fun getMeta(): Meta
suspend fun updateMeta(meta: Meta) suspend fun updateMeta(meta: Meta)
suspend fun getJwtMeta(): Jwt suspend fun getJwtMeta(): Jwt

View File

@ -3,12 +3,12 @@ package dev.usbharu.hideout.service.core
import dev.usbharu.hideout.domain.model.hideout.entity.Jwt import dev.usbharu.hideout.domain.model.hideout.entity.Jwt
import dev.usbharu.hideout.domain.model.hideout.entity.Meta import dev.usbharu.hideout.domain.model.hideout.entity.Meta
import dev.usbharu.hideout.exception.NotInitException import dev.usbharu.hideout.exception.NotInitException
import dev.usbharu.hideout.repository.IMetaRepository import dev.usbharu.hideout.repository.MetaRepository
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
@Single @Single
class MetaServiceImpl(private val metaRepository: IMetaRepository, private val transaction: Transaction) : class MetaServiceImpl(private val metaRepository: MetaRepository, private val transaction: Transaction) :
IMetaService { MetaService {
override suspend fun getMeta(): Meta = override suspend fun getMeta(): Meta =
transaction.transaction { metaRepository.get() ?: throw NotInitException("Meta is null") } transaction.transaction { metaRepository.get() ?: throw NotInitException("Meta is null") }

View File

@ -1,5 +1,5 @@
package dev.usbharu.hideout.service.core package dev.usbharu.hideout.service.core
interface IServerInitialiseService { interface ServerInitialiseService {
suspend fun init() suspend fun init()
} }

View File

@ -2,7 +2,7 @@ package dev.usbharu.hideout.service.core
import dev.usbharu.hideout.domain.model.hideout.entity.Jwt import dev.usbharu.hideout.domain.model.hideout.entity.Jwt
import dev.usbharu.hideout.domain.model.hideout.entity.Meta import dev.usbharu.hideout.domain.model.hideout.entity.Meta
import dev.usbharu.hideout.repository.IMetaRepository import dev.usbharu.hideout.repository.MetaRepository
import dev.usbharu.hideout.util.ServerUtil import dev.usbharu.hideout.util.ServerUtil
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
import org.slf4j.Logger import org.slf4j.Logger
@ -12,10 +12,10 @@ import java.util.*
@Single @Single
class ServerInitialiseServiceImpl( class ServerInitialiseServiceImpl(
private val metaRepository: IMetaRepository, private val metaRepository: MetaRepository,
private val transaction: Transaction private val transaction: Transaction
) : ) :
IServerInitialiseService { ServerInitialiseService {
val logger: Logger = LoggerFactory.getLogger(ServerInitialiseServiceImpl::class.java) val logger: Logger = LoggerFactory.getLogger(ServerInitialiseServiceImpl::class.java)

View File

@ -3,6 +3,6 @@ package dev.usbharu.hideout.service.post
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
import dev.usbharu.hideout.domain.model.hideout.entity.Post import dev.usbharu.hideout.domain.model.hideout.entity.Post
interface IPostService { interface PostService {
suspend fun createLocal(post: PostCreateDto): Post suspend fun createLocal(post: PostCreateDto): Post
} }

View File

@ -3,18 +3,18 @@ package dev.usbharu.hideout.service.post
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
import dev.usbharu.hideout.domain.model.hideout.entity.Post import dev.usbharu.hideout.domain.model.hideout.entity.Post
import dev.usbharu.hideout.exception.UserNotFoundException import dev.usbharu.hideout.exception.UserNotFoundException
import dev.usbharu.hideout.repository.IPostRepository import dev.usbharu.hideout.repository.PostRepository
import dev.usbharu.hideout.repository.IUserRepository import dev.usbharu.hideout.repository.UserRepository
import dev.usbharu.hideout.service.activitypub.ActivityPubNoteService import dev.usbharu.hideout.service.ap.APNoteService
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
import java.time.Instant import java.time.Instant
@Single @Single
class PostServiceImpl( class PostServiceImpl(
private val postRepository: IPostRepository, private val postRepository: PostRepository,
private val userRepository: IUserRepository, private val userRepository: UserRepository,
private val activityPubNoteService: ActivityPubNoteService private val apNoteService: APNoteService
) : IPostService { ) : PostService {
override suspend fun createLocal(post: PostCreateDto): Post { override suspend fun createLocal(post: PostCreateDto): Post {
val user = userRepository.findById(post.userId) ?: throw UserNotFoundException("${post.userId} was not found") val user = userRepository.findById(post.userId) ?: throw UserNotFoundException("${post.userId} was not found")
val id = postRepository.generateId() val id = postRepository.generateId()
@ -29,7 +29,7 @@ class PostServiceImpl(
repostId = null, repostId = null,
replyId = null replyId = null
) )
activityPubNoteService.createNote(createPost) apNoteService.createNote(createPost)
return internalCreate(createPost) return internalCreate(createPost)
} }

View File

@ -1,6 +1,6 @@
package dev.usbharu.hideout.service.reaction package dev.usbharu.hideout.service.reaction
interface IReactionService { interface ReactionService {
suspend fun receiveReaction(name: String, domain: String, userId: Long, postId: Long) suspend fun receiveReaction(name: String, domain: String, userId: Long, postId: Long)
suspend fun sendReaction(name: String, userId: Long, postId: Long) suspend fun sendReaction(name: String, userId: Long, postId: Long)
suspend fun removeReaction(userId: Long, postId: Long) suspend fun removeReaction(userId: Long, postId: Long)

View File

@ -3,15 +3,15 @@ package dev.usbharu.hideout.service.reaction
import dev.usbharu.hideout.domain.model.hideout.entity.Reaction import dev.usbharu.hideout.domain.model.hideout.entity.Reaction
import dev.usbharu.hideout.query.ReactionQueryService import dev.usbharu.hideout.query.ReactionQueryService
import dev.usbharu.hideout.repository.ReactionRepository import dev.usbharu.hideout.repository.ReactionRepository
import dev.usbharu.hideout.service.activitypub.ActivityPubReactionService import dev.usbharu.hideout.service.ap.APReactionService
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
@Single @Single
class ReactionServiceImpl( class ReactionServiceImpl(
private val reactionRepository: ReactionRepository, private val reactionRepository: ReactionRepository,
private val activityPubReactionService: ActivityPubReactionService, private val apReactionService: APReactionService,
private val reactionQueryService: ReactionQueryService private val reactionQueryService: ReactionQueryService
) : IReactionService { ) : ReactionService {
override suspend fun receiveReaction(name: String, domain: String, userId: Long, postId: Long) { override suspend fun receiveReaction(name: String, domain: String, userId: Long, postId: Long) {
if (reactionQueryService.reactionAlreadyExist(postId, userId, 0).not()) { if (reactionQueryService.reactionAlreadyExist(postId, userId, 0).not()) {
reactionRepository.save( reactionRepository.save(
@ -27,7 +27,7 @@ class ReactionServiceImpl(
} else { } else {
val reaction = Reaction(reactionRepository.generateId(), 0, postId, userId) val reaction = Reaction(reactionRepository.generateId(), 0, postId, userId)
reactionRepository.save(reaction) reactionRepository.save(reaction)
activityPubReactionService.reaction(reaction) apReactionService.reaction(reaction)
} }
} }

View File

@ -1,13 +0,0 @@
package dev.usbharu.hideout.service.user
import java.security.KeyPair
interface IUserAuthService {
fun hash(password: String): String
suspend fun usernameAlreadyUse(username: String): Boolean
suspend fun generateKeyPair(): KeyPair
suspend fun verifyAccount(username: String, password: String): Boolean
}

View File

@ -1,34 +0,0 @@
package dev.usbharu.hideout.service.user
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
import dev.usbharu.hideout.domain.model.hideout.entity.User
@Suppress("TooManyFunctions")
interface IUserService {
suspend fun usernameAlreadyUse(username: String): Boolean
suspend fun createLocalUser(user: UserCreateDto): User
suspend fun createRemoteUser(user: RemoteUserCreateDto): User
/**
* フォローリクエストを送信する
*
* @param id
* @param followerId
* @return リクエストが成功したか
*/
suspend fun followRequest(id: Long, followerId: Long): Boolean
/**
* フォローする
*
* @param id
* @param followerId
*/
suspend fun follow(id: Long, followerId: Long)
suspend fun unfollow(id: Long, followerId: Long): Boolean
}

View File

@ -1,53 +1,13 @@
package dev.usbharu.hideout.service.user package dev.usbharu.hideout.service.user
import dev.usbharu.hideout.config.Config import java.security.KeyPair
import dev.usbharu.hideout.query.UserQueryService
import io.ktor.util.*
import org.koin.core.annotation.Single
import java.security.*
import java.util.*
@Single interface UserAuthService {
class UserAuthService( fun hash(password: String): String
val userQueryService: UserQueryService
) : IUserAuthService {
override fun hash(password: String): String { suspend fun usernameAlreadyUse(username: String): Boolean
val digest = sha256.digest(password.toByteArray(Charsets.UTF_8))
return hex(digest)
}
override suspend fun usernameAlreadyUse(username: String): Boolean { suspend fun generateKeyPair(): KeyPair
userQueryService.findByName(username)
return true
}
override suspend fun verifyAccount(username: String, password: String): Boolean { suspend fun verifyAccount(username: String, password: String): Boolean
val userEntity = userQueryService.findByNameAndDomain(username, Config.configData.domain)
return userEntity.password == hash(password)
}
override suspend fun generateKeyPair(): KeyPair {
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
keyPairGenerator.initialize(keySize)
return keyPairGenerator.generateKeyPair()
}
companion object {
val sha256: MessageDigest = MessageDigest.getInstance("SHA-256")
const val keySize = 2048
const val pemSize = 64
}
}
fun PublicKey.toPem(): String {
return "-----BEGIN PUBLIC KEY-----\n" +
Base64.getEncoder().encodeToString(encoded).chunked(UserAuthService.pemSize).joinToString("\n") +
"\n-----END PUBLIC KEY-----\n"
}
fun PrivateKey.toPem(): String {
return "-----BEGIN PRIVATE KEY-----\n" +
Base64.getEncoder().encodeToString(encoded).chunked(UserAuthService.pemSize).joinToString("\n") +
"\n-----END PRIVATE KEY-----\n"
} }

View File

@ -0,0 +1,53 @@
package dev.usbharu.hideout.service.user
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.query.UserQueryService
import io.ktor.util.*
import org.koin.core.annotation.Single
import java.security.*
import java.util.*
@Single
class UserAuthServiceImpl(
val userQueryService: UserQueryService
) : UserAuthService {
override fun hash(password: String): String {
val digest = sha256.digest(password.toByteArray(Charsets.UTF_8))
return hex(digest)
}
override suspend fun usernameAlreadyUse(username: String): Boolean {
userQueryService.findByName(username)
return true
}
override suspend fun verifyAccount(username: String, password: String): Boolean {
val userEntity = userQueryService.findByNameAndDomain(username, Config.configData.domain)
return userEntity.password == hash(password)
}
override suspend fun generateKeyPair(): KeyPair {
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
keyPairGenerator.initialize(keySize)
return keyPairGenerator.generateKeyPair()
}
companion object {
val sha256: MessageDigest = MessageDigest.getInstance("SHA-256")
const val keySize = 2048
const val pemSize = 64
}
}
fun PublicKey.toPem(): String {
return "-----BEGIN PUBLIC KEY-----\n" +
Base64.getEncoder().encodeToString(encoded).chunked(UserAuthServiceImpl.pemSize).joinToString("\n") +
"\n-----END PUBLIC KEY-----\n"
}
fun PrivateKey.toPem(): String {
return "-----BEGIN PRIVATE KEY-----\n" +
Base64.getEncoder().encodeToString(encoded).chunked(UserAuthServiceImpl.pemSize).joinToString("\n") +
"\n-----END PRIVATE KEY-----\n"
}

View File

@ -1,97 +1,34 @@
package dev.usbharu.hideout.service.user package dev.usbharu.hideout.service.user
import dev.usbharu.hideout.config.Config
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.dto.SendFollowDto
import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.exception.UserNotFoundException
import dev.usbharu.hideout.query.FollowerQueryService
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.repository.IUserRepository
import dev.usbharu.hideout.service.activitypub.ActivityPubSendFollowService
import org.koin.core.annotation.Single
import java.time.Instant
@Single @Suppress("TooManyFunctions")
class UserService( interface UserService {
private val userRepository: IUserRepository,
private val userAuthService: IUserAuthService,
private val activityPubSendFollowService: ActivityPubSendFollowService,
private val userQueryService: UserQueryService,
private val followerQueryService: FollowerQueryService
) :
IUserService {
override suspend fun usernameAlreadyUse(username: String): Boolean { suspend fun usernameAlreadyUse(username: String): Boolean
val findByNameAndDomain = userQueryService.findByNameAndDomain(username, Config.configData.domain)
return findByNameAndDomain != null
}
override suspend fun createLocalUser(user: UserCreateDto): User { suspend fun createLocalUser(user: UserCreateDto): User
val nextId = userRepository.nextId()
val hashedPassword = userAuthService.hash(user.password)
val keyPair = userAuthService.generateKeyPair()
val userEntity = User(
id = nextId,
name = user.name,
domain = Config.configData.domain,
screenName = user.screenName,
description = user.description,
password = hashedPassword,
inbox = "${Config.configData.url}/users/${user.name}/inbox",
outbox = "${Config.configData.url}/users/${user.name}/outbox",
url = "${Config.configData.url}/users/${user.name}",
publicKey = keyPair.public.toPem(),
privateKey = keyPair.private.toPem(),
createdAt = Instant.now()
)
return userRepository.save(userEntity)
}
override suspend fun createRemoteUser(user: RemoteUserCreateDto): User { suspend fun createRemoteUser(user: RemoteUserCreateDto): User
val nextId = userRepository.nextId()
val userEntity = User(
id = nextId,
name = user.name,
domain = user.domain,
screenName = user.screenName,
description = user.description,
inbox = user.inbox,
outbox = user.outbox,
url = user.url,
publicKey = user.publicKey,
createdAt = Instant.now()
)
return userRepository.save(userEntity)
}
// TODO APのフォロー処理を作る /**
override suspend fun followRequest(id: Long, followerId: Long): Boolean { * フォローリクエストを送信する
val user = userRepository.findById(id) ?: throw UserNotFoundException("$id was not found.") *
val follower = userRepository.findById(followerId) ?: throw UserNotFoundException("$followerId was not found.") * @param id
return if (user.domain == Config.configData.domain) { * @param followerId
follow(id, followerId) * @return リクエストが成功したか
true */
} else { suspend fun followRequest(id: Long, followerId: Long): Boolean
if (userRepository.findFollowRequestsById(id, followerId)) {
// do-nothing
} else {
activityPubSendFollowService.sendFollow(SendFollowDto(follower, user))
}
false
}
}
override suspend fun follow(id: Long, followerId: Long) { /**
followerQueryService.appendFollower(id, followerId) * フォローする
if (userRepository.findFollowRequestsById(id, followerId)) { *
userRepository.deleteFollowRequest(id, followerId) * @param id
} * @param followerId
} */
suspend fun follow(id: Long, followerId: Long)
override suspend fun unfollow(id: Long, followerId: Long): Boolean { suspend fun unfollow(id: Long, followerId: Long): Boolean
followerQueryService.removeFollower(id, followerId)
return false
}
} }

View File

@ -0,0 +1,97 @@
package dev.usbharu.hideout.service.user
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto
import dev.usbharu.hideout.domain.model.hideout.dto.SendFollowDto
import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.exception.UserNotFoundException
import dev.usbharu.hideout.query.FollowerQueryService
import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.repository.UserRepository
import dev.usbharu.hideout.service.ap.APSendFollowService
import org.koin.core.annotation.Single
import java.time.Instant
@Single
class UserServiceImpl(
private val userRepository: UserRepository,
private val userAuthService: UserAuthService,
private val apSendFollowService: APSendFollowService,
private val userQueryService: UserQueryService,
private val followerQueryService: FollowerQueryService
) :
UserService {
override suspend fun usernameAlreadyUse(username: String): Boolean {
val findByNameAndDomain = userQueryService.findByNameAndDomain(username, Config.configData.domain)
return findByNameAndDomain != null
}
override suspend fun createLocalUser(user: UserCreateDto): User {
val nextId = userRepository.nextId()
val hashedPassword = userAuthService.hash(user.password)
val keyPair = userAuthService.generateKeyPair()
val userEntity = User(
id = nextId,
name = user.name,
domain = Config.configData.domain,
screenName = user.screenName,
description = user.description,
password = hashedPassword,
inbox = "${Config.configData.url}/users/${user.name}/inbox",
outbox = "${Config.configData.url}/users/${user.name}/outbox",
url = "${Config.configData.url}/users/${user.name}",
publicKey = keyPair.public.toPem(),
privateKey = keyPair.private.toPem(),
createdAt = Instant.now()
)
return userRepository.save(userEntity)
}
override suspend fun createRemoteUser(user: RemoteUserCreateDto): User {
val nextId = userRepository.nextId()
val userEntity = User(
id = nextId,
name = user.name,
domain = user.domain,
screenName = user.screenName,
description = user.description,
inbox = user.inbox,
outbox = user.outbox,
url = user.url,
publicKey = user.publicKey,
createdAt = Instant.now()
)
return userRepository.save(userEntity)
}
// TODO APのフォロー処理を作る
override suspend fun followRequest(id: Long, followerId: Long): Boolean {
val user = userRepository.findById(id) ?: throw UserNotFoundException("$id was not found.")
val follower = userRepository.findById(followerId) ?: throw UserNotFoundException("$followerId was not found.")
return if (user.domain == Config.configData.domain) {
follow(id, followerId)
true
} else {
if (userRepository.findFollowRequestsById(id, followerId)) {
// do-nothing
} else {
apSendFollowService.sendFollow(SendFollowDto(follower, user))
}
false
}
}
override suspend fun follow(id: Long, followerId: Long) {
followerQueryService.appendFollower(id, followerId)
if (userRepository.findFollowRequestsById(id, followerId)) {
userRepository.deleteFollowRequest(id, followerId)
}
}
override suspend fun unfollow(id: Long, followerId: Long): Boolean {
followerQueryService.removeFollower(id, followerId)
return false
}
}

View File

@ -18,9 +18,9 @@ import dev.usbharu.hideout.exception.InvalidUsernameOrPasswordException
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.routing.api.internal.v1.auth import dev.usbharu.hideout.routing.api.internal.v1.auth
import dev.usbharu.hideout.service.api.UserAuthApiService import dev.usbharu.hideout.service.api.UserAuthApiService
import dev.usbharu.hideout.service.auth.IJwtService import dev.usbharu.hideout.service.auth.JwtService
import dev.usbharu.hideout.service.core.IMetaService import dev.usbharu.hideout.service.core.MetaService
import dev.usbharu.hideout.service.user.IUserAuthService import dev.usbharu.hideout.service.user.UserAuthService
import dev.usbharu.hideout.util.Base64Util import dev.usbharu.hideout.util.Base64Util
import dev.usbharu.hideout.util.JsonWebKeyUtil import dev.usbharu.hideout.util.JsonWebKeyUtil
import io.ktor.client.request.* import io.ktor.client.request.*
@ -51,7 +51,7 @@ class SecurityKtTest {
val userAuthService = mock<UserAuthApiService> { val userAuthService = mock<UserAuthApiService> {
onBlocking { login(eq("testUser"), eq("password")) } doReturn jwtToken onBlocking { login(eq("testUser"), eq("password")) } doReturn jwtToken
} }
val metaService = mock<IMetaService>() val metaService = mock<MetaService>()
val userQueryService = mock<UserQueryService> { val userQueryService = mock<UserQueryService> {
onBlocking { findByNameAndDomain(eq("testUser"), eq("example.com")) } doReturn User( onBlocking { findByNameAndDomain(eq("testUser"), eq("example.com")) } doReturn User(
id = 1L, id = 1L,
@ -93,12 +93,12 @@ class SecurityKtTest {
config = ApplicationConfig("empty.conf") config = ApplicationConfig("empty.conf")
} }
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper()) Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
mock<IUserAuthService> { mock<UserAuthService> {
onBlocking { verifyAccount(anyString(), anyString()) }.doReturn(false) onBlocking { verifyAccount(anyString(), anyString()) }.doReturn(false)
} }
val metaService = mock<IMetaService>() val metaService = mock<MetaService>()
mock<UserQueryService>() mock<UserQueryService>()
mock<IJwtService>() mock<JwtService>()
val jwkProvider = mock<JwkProvider>() val jwkProvider = mock<JwkProvider>()
val userAuthApiService = mock<UserAuthApiService> { val userAuthApiService = mock<UserAuthApiService> {
onBlocking { login(anyString(), anyString()) } doThrow InvalidUsernameOrPasswordException() onBlocking { login(anyString(), anyString()) } doThrow InvalidUsernameOrPasswordException()
@ -126,7 +126,7 @@ class SecurityKtTest {
config = ApplicationConfig("empty.conf") config = ApplicationConfig("empty.conf")
} }
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper()) Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
val metaService = mock<IMetaService>() val metaService = mock<MetaService>()
val jwkProvider = mock<JwkProvider>() val jwkProvider = mock<JwkProvider>()
val userAuthApiService = mock<UserAuthApiService> { val userAuthApiService = mock<UserAuthApiService> {
onBlocking { login(anyString(), eq("InvalidPassword")) } doThrow InvalidUsernameOrPasswordException() onBlocking { login(anyString(), eq("InvalidPassword")) } doThrow InvalidUsernameOrPasswordException()
@ -247,7 +247,7 @@ class SecurityKtTest {
.withClaim("uid", 123456L) .withClaim("uid", 123456L)
.withExpiresAt(now.plus(30, ChronoUnit.MINUTES)) .withExpiresAt(now.plus(30, ChronoUnit.MINUTES))
.sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey)) .sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey))
val metaService = mock<IMetaService> { val metaService = mock<MetaService> {
onBlocking { getJwtMeta() }.doReturn( onBlocking { getJwtMeta() }.doReturn(
Jwt( Jwt(
kid, kid,
@ -308,7 +308,7 @@ class SecurityKtTest {
.withClaim("uid", 123345L) .withClaim("uid", 123345L)
.withExpiresAt(now.minus(30, ChronoUnit.MINUTES)) .withExpiresAt(now.minus(30, ChronoUnit.MINUTES))
.sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey)) .sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey))
val metaService = mock<IMetaService> { val metaService = mock<MetaService> {
onBlocking { getJwtMeta() }.doReturn( onBlocking { getJwtMeta() }.doReturn(
Jwt( Jwt(
kid, kid,
@ -367,7 +367,7 @@ class SecurityKtTest {
.withClaim("uid", 12345L) .withClaim("uid", 12345L)
.withExpiresAt(now.plus(30, ChronoUnit.MINUTES)) .withExpiresAt(now.plus(30, ChronoUnit.MINUTES))
.sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey)) .sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey))
val metaService = mock<IMetaService> { val metaService = mock<MetaService> {
onBlocking { getJwtMeta() }.doReturn( onBlocking { getJwtMeta() }.doReturn(
Jwt( Jwt(
kid, kid,
@ -426,7 +426,7 @@ class SecurityKtTest {
.withClaim("uid", null as Long?) .withClaim("uid", null as Long?)
.withExpiresAt(now.plus(30, ChronoUnit.MINUTES)) .withExpiresAt(now.plus(30, ChronoUnit.MINUTES))
.sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey)) .sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey))
val metaService = mock<IMetaService> { val metaService = mock<MetaService> {
onBlocking { getJwtMeta() }.doReturn( onBlocking { getJwtMeta() }.doReturn(
Jwt( Jwt(
kid, kid,
@ -484,7 +484,7 @@ class SecurityKtTest {
.withKeyId(kid.toString()) .withKeyId(kid.toString())
.withExpiresAt(now.plus(30, ChronoUnit.MINUTES)) .withExpiresAt(now.plus(30, ChronoUnit.MINUTES))
.sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey)) .sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey))
val metaService = mock<IMetaService> { val metaService = mock<MetaService> {
onBlocking { getJwtMeta() }.doReturn( onBlocking { getJwtMeta() }.doReturn(
Jwt( Jwt(
kid, kid,

View File

@ -3,10 +3,10 @@ package dev.usbharu.hideout.routing.activitypub
import dev.usbharu.hideout.exception.JsonParseException import dev.usbharu.hideout.exception.JsonParseException
import dev.usbharu.hideout.plugins.configureSerialization import dev.usbharu.hideout.plugins.configureSerialization
import dev.usbharu.hideout.plugins.configureStatusPages import dev.usbharu.hideout.plugins.configureStatusPages
import dev.usbharu.hideout.service.activitypub.ActivityPubService import dev.usbharu.hideout.service.ap.APService
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService import dev.usbharu.hideout.service.ap.APUserService
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
import dev.usbharu.hideout.service.user.IUserService import dev.usbharu.hideout.service.user.UserService
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.config.* import io.ktor.server.config.*
@ -44,16 +44,16 @@ class InboxRoutingKtTest {
val httpSignatureVerifyService = mock<HttpSignatureVerifyService> { val httpSignatureVerifyService = mock<HttpSignatureVerifyService> {
on { verify(any()) } doReturn true on { verify(any()) } doReturn true
} }
val activityPubService = mock<ActivityPubService> { val apService = mock<APService> {
on { parseActivity(any()) } doThrow JsonParseException() on { parseActivity(any()) } doThrow JsonParseException()
} }
mock<IUserService>() mock<UserService>()
mock<ActivityPubUserService>() mock<APUserService>()
application { application {
configureStatusPages() configureStatusPages()
configureSerialization() configureSerialization()
routing { routing {
inbox(httpSignatureVerifyService, activityPubService) inbox(httpSignatureVerifyService, apService)
} }
} }
client.post("/inbox").let { client.post("/inbox").let {
@ -85,16 +85,16 @@ class InboxRoutingKtTest {
val httpSignatureVerifyService = mock<HttpSignatureVerifyService> { val httpSignatureVerifyService = mock<HttpSignatureVerifyService> {
on { verify(any()) } doReturn true on { verify(any()) } doReturn true
} }
val activityPubService = mock<ActivityPubService> { val apService = mock<APService> {
on { parseActivity(any()) } doThrow JsonParseException() on { parseActivity(any()) } doThrow JsonParseException()
} }
mock<IUserService>() mock<UserService>()
mock<ActivityPubUserService>() mock<APUserService>()
application { application {
configureStatusPages() configureStatusPages()
configureSerialization() configureSerialization()
routing { routing {
inbox(httpSignatureVerifyService, activityPubService) inbox(httpSignatureVerifyService, apService)
} }
} }
client.post("/users/test/inbox").let { client.post("/users/test/inbox").let {

View File

@ -12,7 +12,7 @@ import dev.usbharu.hideout.domain.model.ap.Person
import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.plugins.configureSerialization import dev.usbharu.hideout.plugins.configureSerialization
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService import dev.usbharu.hideout.service.ap.APUserService
import dev.usbharu.hideout.util.HttpUtil.Activity import dev.usbharu.hideout.util.HttpUtil.Activity
import dev.usbharu.hideout.util.HttpUtil.JsonLd import dev.usbharu.hideout.util.HttpUtil.JsonLd
import io.ktor.client.request.* import io.ktor.client.request.*
@ -63,14 +63,14 @@ class UsersAPTest {
) )
person.context = listOf("https://www.w3.org/ns/activitystreams") person.context = listOf("https://www.w3.org/ns/activitystreams")
val activityPubUserService = mock<ActivityPubUserService> { val apUserService = mock<APUserService> {
onBlocking { getPersonByName(anyString()) } doReturn person onBlocking { getPersonByName(anyString()) } doReturn person
} }
application { application {
configureSerialization() configureSerialization()
routing { routing {
usersAP(activityPubUserService, mock(), mock(), TestTransaction) usersAP(apUserService, mock(), mock(), TestTransaction)
} }
} }
client.get("/users/test") { client.get("/users/test") {
@ -121,14 +121,14 @@ class UsersAPTest {
) )
person.context = listOf("https://www.w3.org/ns/activitystreams") person.context = listOf("https://www.w3.org/ns/activitystreams")
val activityPubUserService = mock<ActivityPubUserService> { val apUserService = mock<APUserService> {
onBlocking { getPersonByName(anyString()) } doReturn person onBlocking { getPersonByName(anyString()) } doReturn person
} }
application { application {
configureSerialization() configureSerialization()
routing { routing {
usersAP(activityPubUserService, mock(), mock(), TestTransaction) usersAP(apUserService, mock(), mock(), TestTransaction)
} }
} }
client.get("/users/test") { client.get("/users/test") {

View File

@ -10,7 +10,7 @@ import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
import dev.usbharu.hideout.plugins.TOKEN_AUTH import dev.usbharu.hideout.plugins.TOKEN_AUTH
import dev.usbharu.hideout.plugins.configureSecurity import dev.usbharu.hideout.plugins.configureSecurity
import dev.usbharu.hideout.plugins.configureSerialization import dev.usbharu.hideout.plugins.configureSerialization
import dev.usbharu.hideout.service.api.IPostApiService import dev.usbharu.hideout.service.api.PostApiService
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.statement.* import io.ktor.client.statement.*
import io.ktor.http.* import io.ktor.http.*
@ -60,7 +60,7 @@ class PostsTest {
url = "https://example.com/posts/2" url = "https://example.com/posts/2"
) )
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { onBlocking {
getAll( getAll(
since = anyOrNull(), since = anyOrNull(),
@ -135,7 +135,7 @@ class PostsTest {
) )
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { onBlocking {
getAll( getAll(
since = anyOrNull(), since = anyOrNull(),
@ -191,7 +191,7 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(), createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/1" url = "https://example.com/posts/1"
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { getById(any(), anyOrNull()) } doReturn post onBlocking { getById(any(), anyOrNull()) } doReturn post
} }
application { application {
@ -230,7 +230,7 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(), createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/1" url = "https://example.com/posts/1"
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { getById(any(), isNotNull()) } doReturn post onBlocking { getById(any(), isNotNull()) } doReturn post
} }
val claim = mock<Claim> { val claim = mock<Claim> {
@ -273,7 +273,7 @@ class PostsTest {
val payload = mock<Payload> { val payload = mock<Payload> {
on { getClaim(eq("uid")) } doReturn claim on { getClaim(eq("uid")) } doReturn claim
} }
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { createPost(any(), any()) } doAnswer { onBlocking { createPost(any(), any()) } doAnswer {
val argument = it.getArgument<dev.usbharu.hideout.domain.model.hideout.form.Post>(0) val argument = it.getArgument<dev.usbharu.hideout.domain.model.hideout.form.Post>(0)
val userId = it.getArgument<Long>(1) val userId = it.getArgument<Long>(1)
@ -360,7 +360,7 @@ class PostsTest {
url = "https://example.com/posts/2" url = "https://example.com/posts/2"
) )
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { onBlocking {
getByUser( getByUser(
nameOrId = any(), nameOrId = any(),
@ -421,7 +421,7 @@ class PostsTest {
url = "https://example.com/posts/2" url = "https://example.com/posts/2"
) )
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { onBlocking {
getByUser( getByUser(
nameOrId = eq("test1"), nameOrId = eq("test1"),
@ -482,7 +482,7 @@ class PostsTest {
url = "https://example.com/posts/2" url = "https://example.com/posts/2"
) )
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { onBlocking {
getByUser( getByUser(
nameOrId = eq("test1@example.com"), nameOrId = eq("test1@example.com"),
@ -543,7 +543,7 @@ class PostsTest {
url = "https://example.com/posts/2" url = "https://example.com/posts/2"
) )
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { onBlocking {
getByUser( getByUser(
nameOrId = eq("@test1@example.com"), nameOrId = eq("@test1@example.com"),
@ -593,7 +593,7 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(), createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/2" url = "https://example.com/posts/2"
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
} }
application { application {
@ -633,7 +633,7 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(), createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/2" url = "https://example.com/posts/2"
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
} }
application { application {
@ -673,7 +673,7 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(), createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/2" url = "https://example.com/posts/2"
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
} }
application { application {
@ -713,7 +713,7 @@ class PostsTest {
createdAt = Instant.now().toEpochMilli(), createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/2" url = "https://example.com/posts/2"
) )
val postService = mock<IPostApiService> { val postService = mock<PostApiService> {
onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post
} }
application { application {

View File

@ -10,8 +10,8 @@ import dev.usbharu.hideout.domain.model.hideout.form.UserCreate
import dev.usbharu.hideout.plugins.TOKEN_AUTH import dev.usbharu.hideout.plugins.TOKEN_AUTH
import dev.usbharu.hideout.plugins.configureSecurity import dev.usbharu.hideout.plugins.configureSecurity
import dev.usbharu.hideout.plugins.configureSerialization import dev.usbharu.hideout.plugins.configureSerialization
import dev.usbharu.hideout.service.api.IUserApiService import dev.usbharu.hideout.service.api.UserApiService
import dev.usbharu.hideout.service.user.IUserService import dev.usbharu.hideout.service.user.UserService
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.statement.* import io.ktor.client.statement.*
import io.ktor.http.* import io.ktor.http.*
@ -53,7 +53,7 @@ class UsersTest {
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
), ),
) )
val userService = mock<IUserApiService> { val userService = mock<UserApiService> {
onBlocking { findAll(anyOrNull(), anyOrNull()) } doReturn users onBlocking { findAll(anyOrNull(), anyOrNull()) } doReturn users
} }
application { application {
@ -77,7 +77,7 @@ class UsersTest {
config = ApplicationConfig("empty.conf") config = ApplicationConfig("empty.conf")
} }
val userCreateDto = UserCreate("test", "XXXXXXX") val userCreateDto = UserCreate("test", "XXXXXXX")
val userService = mock<IUserService> { val userService = mock<UserService> {
onBlocking { usernameAlreadyUse(any()) } doReturn false onBlocking { usernameAlreadyUse(any()) } doReturn false
onBlocking { createLocalUser(any()) } doReturn User( onBlocking { createLocalUser(any()) } doReturn User(
id = 12345, id = 12345,
@ -122,7 +122,7 @@ class UsersTest {
config = ApplicationConfig("empty.conf") config = ApplicationConfig("empty.conf")
} }
val userCreateDto = UserCreate("test", "XXXXXXX") val userCreateDto = UserCreate("test", "XXXXXXX")
val userService = mock<IUserService> { val userService = mock<UserService> {
onBlocking { usernameAlreadyUse(any()) } doReturn true onBlocking { usernameAlreadyUse(any()) } doReturn true
} }
application { application {
@ -157,7 +157,7 @@ class UsersTest {
"https://example.com/test", "https://example.com/test",
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findByAcct(any()) } doReturn userResponse onBlocking { findByAcct(any()) } doReturn userResponse
} }
application { application {
@ -190,7 +190,7 @@ class UsersTest {
"https://example.com/test", "https://example.com/test",
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findById(any()) } doReturn userResponse onBlocking { findById(any()) } doReturn userResponse
} }
application { application {
@ -223,7 +223,7 @@ class UsersTest {
"https://example.com/test", "https://example.com/test",
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findByAcct(any()) } doReturn userResponse onBlocking { findByAcct(any()) } doReturn userResponse
} }
application { application {
@ -256,7 +256,7 @@ class UsersTest {
"https://example.com/test", "https://example.com/test",
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findByAcct(any()) } doReturn userResponse onBlocking { findByAcct(any()) } doReturn userResponse
} }
application { application {
@ -301,7 +301,7 @@ class UsersTest {
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
) )
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findFollowersByAcct(any()) } doReturn followers onBlocking { findFollowersByAcct(any()) } doReturn followers
} }
application { application {
@ -346,7 +346,7 @@ class UsersTest {
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
) )
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findFollowersByAcct(any()) } doReturn followers onBlocking { findFollowersByAcct(any()) } doReturn followers
} }
application { application {
@ -391,7 +391,7 @@ class UsersTest {
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
) )
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findFollowers(any()) } doReturn followers onBlocking { findFollowers(any()) } doReturn followers
} }
application { application {
@ -423,7 +423,7 @@ class UsersTest {
on { getClaim(eq("uid")) } doReturn claim on { getClaim(eq("uid")) } doReturn claim
} }
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findByAcct(any()) } doReturn UserResponse( onBlocking { findByAcct(any()) } doReturn UserResponse(
"1235", "1235",
"follower1", "follower1",
@ -434,7 +434,7 @@ class UsersTest {
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
} }
val userService = mock<IUserService> { val userService = mock<UserService> {
onBlocking { followRequest(eq(1235), eq(1234)) } doReturn true onBlocking { followRequest(eq(1235), eq(1234)) } doReturn true
} }
application { application {
@ -473,7 +473,7 @@ class UsersTest {
on { getClaim(eq("uid")) } doReturn claim on { getClaim(eq("uid")) } doReturn claim
} }
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findByAcct(any()) } doReturn UserResponse( onBlocking { findByAcct(any()) } doReturn UserResponse(
"1235", "1235",
"follower1", "follower1",
@ -484,7 +484,7 @@ class UsersTest {
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
} }
val userService = mock<IUserService> { val userService = mock<UserService> {
onBlocking { followRequest(eq(1235), eq(1234)) } doReturn false onBlocking { followRequest(eq(1235), eq(1234)) } doReturn false
} }
application { application {
@ -523,7 +523,7 @@ class UsersTest {
on { getClaim(eq("uid")) } doReturn claim on { getClaim(eq("uid")) } doReturn claim
} }
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findById(any()) } doReturn UserResponse( onBlocking { findById(any()) } doReturn UserResponse(
"1235", "1235",
"follower1", "follower1",
@ -534,7 +534,7 @@ class UsersTest {
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
} }
val userService = mock<IUserService> { val userService = mock<UserService> {
onBlocking { followRequest(eq(1235), eq(1234)) } doReturn false onBlocking { followRequest(eq(1235), eq(1234)) } doReturn false
} }
application { application {
@ -586,7 +586,7 @@ class UsersTest {
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
) )
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findFollowingsByAcct(any()) } doReturn followers onBlocking { findFollowingsByAcct(any()) } doReturn followers
} }
application { application {
@ -631,7 +631,7 @@ class UsersTest {
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
) )
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findFollowingsByAcct(any()) } doReturn followers onBlocking { findFollowingsByAcct(any()) } doReturn followers
} }
application { application {
@ -676,7 +676,7 @@ class UsersTest {
Instant.now().toEpochMilli() Instant.now().toEpochMilli()
) )
) )
val userApiService = mock<IUserApiService> { val userApiService = mock<UserApiService> {
onBlocking { findFollowings(any()) } doReturn followers onBlocking { findFollowings(any()) } doReturn followers
} }
application { application {

View File

@ -1,7 +1,7 @@
@file:OptIn(ExperimentalCoroutinesApi::class) @file:OptIn(ExperimentalCoroutinesApi::class)
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import dev.usbharu.hideout.config.Config import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.config.ConfigData import dev.usbharu.hideout.config.ConfigData
@ -25,7 +25,7 @@ import utils.JsonObjectMapper
import java.time.Instant import java.time.Instant
import kotlin.test.assertEquals import kotlin.test.assertEquals
class ActivityPubNoteServiceImplTest { class APNoteServiceImplTest {
@Test @Test
fun `createPost 新しい投稿`() = runTest { fun `createPost 新しい投稿`() = runTest {
val followers = listOf<User>( val followers = listOf<User>(
@ -76,7 +76,7 @@ class ActivityPubNoteServiceImplTest {
} }
val jobQueueParentService = mock<JobQueueParentService>() val jobQueueParentService = mock<JobQueueParentService>()
val activityPubNoteService = val activityPubNoteService =
ActivityPubNoteServiceImpl( APNoteServiceImpl(
mock(), mock(),
jobQueueParentService, jobQueueParentService,
mock(), mock(),
@ -107,7 +107,7 @@ class ActivityPubNoteServiceImplTest {
respondOk() respondOk()
} }
) )
val activityPubNoteService = ActivityPubNoteServiceImpl( val activityPubNoteService = APNoteServiceImpl(
httpClient, httpClient,
mock(), mock(),
mock(), mock(),

View File

@ -1,7 +1,7 @@
@file:OptIn(ExperimentalCoroutinesApi::class) @file:OptIn(ExperimentalCoroutinesApi::class)
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package dev.usbharu.hideout.service.activitypub package dev.usbharu.hideout.service.ap
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.config.Config import dev.usbharu.hideout.config.Config
@ -11,7 +11,7 @@ import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.service.job.JobQueueParentService import dev.usbharu.hideout.service.job.JobQueueParentService
import dev.usbharu.hideout.service.user.IUserService import dev.usbharu.hideout.service.user.UserService
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.engine.mock.* import io.ktor.client.engine.mock.*
import kjob.core.dsl.ScheduleContext import kjob.core.dsl.ScheduleContext
@ -27,14 +27,14 @@ import utils.JsonObjectMapper
import utils.TestTransaction import utils.TestTransaction
import java.time.Instant import java.time.Instant
class ActivityPubReceiveFollowServiceImplTest { class APReceiveFollowServiceImplTest {
@Test @Test
fun `receiveFollow フォロー受付処理`() = runTest { fun `receiveFollow フォロー受付処理`() = runTest {
val jobQueueParentService = mock<JobQueueParentService> { val jobQueueParentService = mock<JobQueueParentService> {
onBlocking { schedule(eq(ReceiveFollowJob), any()) } doReturn Unit onBlocking { schedule(eq(ReceiveFollowJob), any()) } doReturn Unit
} }
val activityPubFollowService = val activityPubFollowService =
ActivityPubReceiveFollowServiceImpl(jobQueueParentService, mock(), mock(), mock(), mock(), TestTransaction) APReceiveFollowServiceImpl(jobQueueParentService, mock(), mock(), mock(), mock(), TestTransaction)
activityPubFollowService.receiveFollow( activityPubFollowService.receiveFollow(
Follow( Follow(
emptyList(), emptyList(),
@ -96,7 +96,7 @@ class ActivityPubReceiveFollowServiceImplTest {
) )
) )
val activityPubUserService = mock<ActivityPubUserService> { val apUserService = mock<APUserService> {
onBlocking { fetchPerson(anyString(), any()) } doReturn person onBlocking { fetchPerson(anyString(), any()) } doReturn person
} }
val userQueryService = mock<UserQueryService> { val userQueryService = mock<UserQueryService> {
@ -128,13 +128,13 @@ class ActivityPubReceiveFollowServiceImplTest {
) )
} }
val userService = mock<IUserService> { val userService = mock<UserService> {
onBlocking { followRequest(any(), any()) } doReturn false onBlocking { followRequest(any(), any()) } doReturn false
} }
val activityPubFollowService = val activityPubFollowService =
ActivityPubReceiveFollowServiceImpl( APReceiveFollowServiceImpl(
mock(), mock(),
activityPubUserService, apUserService,
userService, userService,
HttpClient( HttpClient(
MockEngine { httpRequestData -> MockEngine { httpRequestData ->

View File

@ -14,8 +14,8 @@ import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
import dev.usbharu.hideout.exception.InvalidRefreshTokenException import dev.usbharu.hideout.exception.InvalidRefreshTokenException
import dev.usbharu.hideout.query.JwtRefreshTokenQueryService import dev.usbharu.hideout.query.JwtRefreshTokenQueryService
import dev.usbharu.hideout.query.UserQueryService import dev.usbharu.hideout.query.UserQueryService
import dev.usbharu.hideout.repository.IJwtRefreshTokenRepository import dev.usbharu.hideout.repository.JwtRefreshTokenRepository
import dev.usbharu.hideout.service.core.IMetaService import dev.usbharu.hideout.service.core.MetaService
import dev.usbharu.hideout.util.Base64Util import dev.usbharu.hideout.util.Base64Util
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@ -42,14 +42,14 @@ class JwtServiceImplTest {
keyPairGenerator.initialize(2048) keyPairGenerator.initialize(2048)
val generateKeyPair = keyPairGenerator.generateKeyPair() val generateKeyPair = keyPairGenerator.generateKeyPair()
val metaService = mock<IMetaService> { val metaService = mock<MetaService> {
onBlocking { getJwtMeta() } doReturn Jwt( onBlocking { getJwtMeta() } doReturn Jwt(
kid, kid,
Base64Util.encode(generateKeyPair.private.encoded), Base64Util.encode(generateKeyPair.private.encoded),
Base64Util.encode(generateKeyPair.public.encoded) Base64Util.encode(generateKeyPair.public.encoded)
) )
} }
val refreshTokenRepository = mock<IJwtRefreshTokenRepository> { val refreshTokenRepository = mock<JwtRefreshTokenRepository> {
onBlocking { generateId() } doReturn 1L onBlocking { generateId() } doReturn 1L
} }
val jwtService = JwtServiceImpl(metaService, refreshTokenRepository, mock(), mock()) val jwtService = JwtServiceImpl(metaService, refreshTokenRepository, mock(), mock())
@ -94,7 +94,7 @@ class JwtServiceImplTest {
keyPairGenerator.initialize(2048) keyPairGenerator.initialize(2048)
val generateKeyPair = keyPairGenerator.generateKeyPair() val generateKeyPair = keyPairGenerator.generateKeyPair()
val refreshTokenRepository = mock<IJwtRefreshTokenRepository> { val refreshTokenRepository = mock<JwtRefreshTokenRepository> {
onBlocking { generateId() } doReturn 2L onBlocking { generateId() } doReturn 2L
} }
@ -123,7 +123,7 @@ class JwtServiceImplTest {
createdAt = Instant.now() createdAt = Instant.now()
) )
} }
val metaService = mock<IMetaService> { val metaService = mock<MetaService> {
onBlocking { getJwtMeta() } doReturn Jwt( onBlocking { getJwtMeta() } doReturn Jwt(
kid, kid,
Base64Util.encode(generateKeyPair.private.encoded), Base64Util.encode(generateKeyPair.private.encoded),
@ -160,7 +160,7 @@ class JwtServiceImplTest {
keyPairGenerator.initialize(2048) keyPairGenerator.initialize(2048)
val generateKeyPair = keyPairGenerator.generateKeyPair() val generateKeyPair = keyPairGenerator.generateKeyPair()
val metaService = mock<IMetaService> { val metaService = mock<MetaService> {
onBlocking { getJwtMeta() } doReturn Jwt( onBlocking { getJwtMeta() } doReturn Jwt(
kid, kid,
Base64Util.encode(generateKeyPair.private.encoded), Base64Util.encode(generateKeyPair.private.encoded),
@ -187,7 +187,7 @@ class JwtServiceImplTest {
keyPairGenerator.initialize(2048) keyPairGenerator.initialize(2048)
val generateKeyPair = keyPairGenerator.generateKeyPair() val generateKeyPair = keyPairGenerator.generateKeyPair()
val metaService = mock<IMetaService> { val metaService = mock<MetaService> {
onBlocking { getJwtMeta() } doReturn Jwt( onBlocking { getJwtMeta() } doReturn Jwt(
kid, kid,
Base64Util.encode(generateKeyPair.private.encoded), Base64Util.encode(generateKeyPair.private.encoded),
@ -214,7 +214,7 @@ class JwtServiceImplTest {
keyPairGenerator.initialize(2048) keyPairGenerator.initialize(2048)
val generateKeyPair = keyPairGenerator.generateKeyPair() val generateKeyPair = keyPairGenerator.generateKeyPair()
val metaService = mock<IMetaService> { val metaService = mock<MetaService> {
onBlocking { getJwtMeta() } doReturn Jwt( onBlocking { getJwtMeta() } doReturn Jwt(
kid, kid,
Base64Util.encode(generateKeyPair.private.encoded), Base64Util.encode(generateKeyPair.private.encoded),

View File

@ -5,7 +5,7 @@ package dev.usbharu.hideout.service.core
import dev.usbharu.hideout.domain.model.hideout.entity.Jwt import dev.usbharu.hideout.domain.model.hideout.entity.Jwt
import dev.usbharu.hideout.domain.model.hideout.entity.Meta import dev.usbharu.hideout.domain.model.hideout.entity.Meta
import dev.usbharu.hideout.exception.NotInitException import dev.usbharu.hideout.exception.NotInitException
import dev.usbharu.hideout.repository.IMetaRepository import dev.usbharu.hideout.repository.MetaRepository
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -19,7 +19,7 @@ class MetaServiceImplTest {
@Test @Test
fun `getMeta メタデータを取得できる`() = runTest { fun `getMeta メタデータを取得できる`() = runTest {
val meta = Meta("1.0.0", Jwt(UUID.randomUUID(), "sdfsdjk", "adafda")) val meta = Meta("1.0.0", Jwt(UUID.randomUUID(), "sdfsdjk", "adafda"))
val metaRepository = mock<IMetaRepository> { val metaRepository = mock<MetaRepository> {
onBlocking { get() } doReturn meta onBlocking { get() } doReturn meta
} }
val metaService = MetaServiceImpl(metaRepository, TestTransaction) val metaService = MetaServiceImpl(metaRepository, TestTransaction)
@ -29,7 +29,7 @@ class MetaServiceImplTest {
@Test @Test
fun `getMeta メタデータが無いときはNotInitExceptionがthrowされる`() = runTest { fun `getMeta メタデータが無いときはNotInitExceptionがthrowされる`() = runTest {
val metaRepository = mock<IMetaRepository> { val metaRepository = mock<MetaRepository> {
onBlocking { get() } doReturn null onBlocking { get() } doReturn null
} }
val metaService = MetaServiceImpl(metaRepository, TestTransaction) val metaService = MetaServiceImpl(metaRepository, TestTransaction)
@ -39,7 +39,7 @@ class MetaServiceImplTest {
@Test @Test
fun `updateMeta メタデータを保存できる`() = runTest { fun `updateMeta メタデータを保存できる`() = runTest {
val meta = Meta("1.0.1", Jwt(UUID.randomUUID(), "sdfsdjk", "adafda")) val meta = Meta("1.0.1", Jwt(UUID.randomUUID(), "sdfsdjk", "adafda"))
val metaRepository = mock<IMetaRepository> { val metaRepository = mock<MetaRepository> {
onBlocking { save(any()) } doReturn Unit onBlocking { save(any()) } doReturn Unit
} }
val metaServiceImpl = MetaServiceImpl(metaRepository, TestTransaction) val metaServiceImpl = MetaServiceImpl(metaRepository, TestTransaction)
@ -53,7 +53,7 @@ class MetaServiceImplTest {
@Test @Test
fun `getJwtMeta Jwtメタデータを取得できる`() = runTest { fun `getJwtMeta Jwtメタデータを取得できる`() = runTest {
val meta = Meta("1.0.0", Jwt(UUID.randomUUID(), "sdfsdjk", "adafda")) val meta = Meta("1.0.0", Jwt(UUID.randomUUID(), "sdfsdjk", "adafda"))
val metaRepository = mock<IMetaRepository> { val metaRepository = mock<MetaRepository> {
onBlocking { get() } doReturn meta onBlocking { get() } doReturn meta
} }
val metaService = MetaServiceImpl(metaRepository, TestTransaction) val metaService = MetaServiceImpl(metaRepository, TestTransaction)
@ -63,7 +63,7 @@ class MetaServiceImplTest {
@Test @Test
fun `getJwtMeta メタデータが無いときはNotInitExceptionがthrowされる`() = runTest { fun `getJwtMeta メタデータが無いときはNotInitExceptionがthrowされる`() = runTest {
val metaRepository = mock<IMetaRepository> { val metaRepository = mock<MetaRepository> {
onBlocking { get() } doReturn null onBlocking { get() } doReturn null
} }
val metaService = MetaServiceImpl(metaRepository, TestTransaction) val metaService = MetaServiceImpl(metaRepository, TestTransaction)

View File

@ -4,7 +4,7 @@ package dev.usbharu.hideout.service.core
import dev.usbharu.hideout.domain.model.hideout.entity.Jwt import dev.usbharu.hideout.domain.model.hideout.entity.Jwt
import dev.usbharu.hideout.domain.model.hideout.entity.Meta import dev.usbharu.hideout.domain.model.hideout.entity.Meta
import dev.usbharu.hideout.repository.IMetaRepository import dev.usbharu.hideout.repository.MetaRepository
import dev.usbharu.hideout.util.ServerUtil import dev.usbharu.hideout.util.ServerUtil
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@ -17,7 +17,7 @@ import kotlin.test.assertEquals
class ServerInitialiseServiceImplTest { class ServerInitialiseServiceImplTest {
@Test @Test
fun `init メタデータが無いときに初期化を実行する`() = runTest { fun `init メタデータが無いときに初期化を実行する`() = runTest {
val metaRepository = mock<IMetaRepository> { val metaRepository = mock<MetaRepository> {
onBlocking { get() } doReturn null onBlocking { get() } doReturn null
onBlocking { save(any()) } doReturn Unit onBlocking { save(any()) } doReturn Unit
} }
@ -30,7 +30,7 @@ class ServerInitialiseServiceImplTest {
@Test @Test
fun `init メタデータが存在して同じバージョンのときは何もしない`() = runTest { fun `init メタデータが存在して同じバージョンのときは何もしない`() = runTest {
val meta = Meta(ServerUtil.getImplementationVersion(), Jwt(UUID.randomUUID(), "aaafafd", "afafasdf")) val meta = Meta(ServerUtil.getImplementationVersion(), Jwt(UUID.randomUUID(), "aaafafd", "afafasdf"))
val metaRepository = mock<IMetaRepository> { val metaRepository = mock<MetaRepository> {
onBlocking { get() } doReturn meta onBlocking { get() } doReturn meta
} }
val serverInitialiseServiceImpl = ServerInitialiseServiceImpl(metaRepository, TestTransaction) val serverInitialiseServiceImpl = ServerInitialiseServiceImpl(metaRepository, TestTransaction)
@ -41,7 +41,7 @@ class ServerInitialiseServiceImplTest {
@Test @Test
fun `init メタデータが存在して違うバージョンのときはバージョンを変更する`() = runTest { fun `init メタデータが存在して違うバージョンのときはバージョンを変更する`() = runTest {
val meta = Meta("1.0.0", Jwt(UUID.randomUUID(), "aaafafd", "afafasdf")) val meta = Meta("1.0.0", Jwt(UUID.randomUUID(), "aaafafd", "afafasdf"))
val metaRepository = mock<IMetaRepository> { val metaRepository = mock<MetaRepository> {
onBlocking { get() } doReturn meta onBlocking { get() } doReturn meta
onBlocking { save(any()) } doReturn Unit onBlocking { save(any()) } doReturn Unit
} }

View File

@ -6,7 +6,7 @@ import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.config.ConfigData import dev.usbharu.hideout.config.ConfigData
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.dto.UserCreateDto import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto
import dev.usbharu.hideout.repository.IUserRepository import dev.usbharu.hideout.repository.UserRepository
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -20,15 +20,15 @@ class UserServiceTest {
@Test @Test
fun `createLocalUser ローカルユーザーを作成できる`() = runTest { fun `createLocalUser ローカルユーザーを作成できる`() = runTest {
Config.configData = ConfigData(domain = "example.com", url = "https://example.com") Config.configData = ConfigData(domain = "example.com", url = "https://example.com")
val userRepository = mock<IUserRepository> { val userRepository = mock<UserRepository> {
onBlocking { nextId() } doReturn 110001L onBlocking { nextId() } doReturn 110001L
} }
val generateKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair() val generateKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair()
val userAuthService = mock<IUserAuthService> { val userAuthService = mock<UserAuthService> {
onBlocking { hash(anyString()) } doReturn "hashedPassword" onBlocking { hash(anyString()) } doReturn "hashedPassword"
onBlocking { generateKeyPair() } doReturn generateKeyPair onBlocking { generateKeyPair() } doReturn generateKeyPair
} }
val userService = UserService(userRepository, userAuthService, mock(), mock(), mock()) val userService = UserServiceImpl(userRepository, userAuthService, mock(), mock(), mock())
userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test")) userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test"))
verify(userRepository, times(1)).save(any()) verify(userRepository, times(1)).save(any())
argumentCaptor<dev.usbharu.hideout.domain.model.hideout.entity.User> { argumentCaptor<dev.usbharu.hideout.domain.model.hideout.entity.User> {
@ -51,10 +51,10 @@ class UserServiceTest {
fun `createRemoteUser リモートユーザーを作成できる`() = runTest { fun `createRemoteUser リモートユーザーを作成できる`() = runTest {
Config.configData = ConfigData(domain = "example.com", url = "https://example.com") Config.configData = ConfigData(domain = "example.com", url = "https://example.com")
val userRepository = mock<IUserRepository> { val userRepository = mock<UserRepository> {
onBlocking { nextId() } doReturn 113345L onBlocking { nextId() } doReturn 113345L
} }
val userService = UserService(userRepository, mock(), mock(), mock(), mock()) val userService = UserServiceImpl(userRepository, mock(), mock(), mock(), mock())
val user = RemoteUserCreateDto( val user = RemoteUserCreateDto(
"test", "test",
"example.com", "example.com",