mirror of https://github.com/usbharu/Hideout.git
commit
daf6acf4f8
|
@ -9,6 +9,7 @@ plugins {
|
||||||
kotlin("jvm") version "1.8.21"
|
kotlin("jvm") version "1.8.21"
|
||||||
id("io.ktor.plugin") version "2.3.0"
|
id("io.ktor.plugin") version "2.3.0"
|
||||||
id("org.graalvm.buildtools.native") version "0.9.21"
|
id("org.graalvm.buildtools.native") version "0.9.21"
|
||||||
|
id("io.gitlab.arturbosch.detekt") version "1.22.0"
|
||||||
// id("org.jetbrains.kotlin.plugin.serialization") version "1.8.10"
|
// id("org.jetbrains.kotlin.plugin.serialization") version "1.8.10"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +89,7 @@ dependencies {
|
||||||
|
|
||||||
testImplementation("org.slf4j:slf4j-simple:2.0.7")
|
testImplementation("org.slf4j:slf4j-simple:2.0.7")
|
||||||
|
|
||||||
|
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.22.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
jib {
|
jib {
|
||||||
|
@ -129,3 +131,11 @@ graalvmNative {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detekt {
|
||||||
|
parallel = true
|
||||||
|
config = files("detekt.yml")
|
||||||
|
buildUponDefaultConfig = true
|
||||||
|
basePath = rootDir.absolutePath
|
||||||
|
autoCorrect = true
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
build:
|
||||||
|
maxIssues: 20
|
||||||
|
|
||||||
|
style:
|
||||||
|
ClassOrdering:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
MandatoryBracesIfStatements:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
MandatoryBracesLoops:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
MultilineLambdaItParameter:
|
||||||
|
active: false
|
||||||
|
|
||||||
|
UseEmptyCounterpart:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
ExpressionBodySyntax:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
WildcardImport:
|
||||||
|
active: false
|
||||||
|
|
||||||
|
ReturnCount:
|
||||||
|
active: false
|
||||||
|
|
||||||
|
MagicNumber:
|
||||||
|
ignorePropertyDeclaration: true
|
||||||
|
|
||||||
|
ForbiddenComment:
|
||||||
|
active: false
|
||||||
|
|
||||||
|
complexity:
|
||||||
|
CognitiveComplexMethod:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
ComplexCondition:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
ComplexInterface:
|
||||||
|
active: true
|
||||||
|
threshold: 30
|
||||||
|
|
||||||
|
LabeledExpression:
|
||||||
|
active: false
|
||||||
|
|
||||||
|
NamedArguments:
|
||||||
|
active: true
|
||||||
|
ignoreArgumentsMatchingNames: true
|
||||||
|
threshold: 5
|
||||||
|
|
||||||
|
NestedBlockDepth:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
NestedScopeFunctions:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
ReplaceSafeCallChainWithRun:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
StringLiteralDuplication:
|
||||||
|
active: false
|
||||||
|
|
||||||
|
LongParameterList:
|
||||||
|
constructorThreshold: 10
|
||||||
|
|
||||||
|
TooManyFunctions:
|
||||||
|
ignoreDeprecated: true
|
||||||
|
ignoreOverridden: true
|
||||||
|
ignorePrivate: true
|
||||||
|
|
||||||
|
LongMethod:
|
||||||
|
active: true
|
||||||
|
excludes:
|
||||||
|
- "**/test/**"
|
||||||
|
|
||||||
|
exceptions:
|
||||||
|
ExceptionRaisedInUnexpectedLocation:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
NotImplementedDeclaration:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
ObjectExtendsThrowable:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
ThrowingExceptionInMain:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
ThrowingExceptionsWithoutMessageOrCause:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
ThrowingNewInstanceOfSameException:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
TooGenericExceptionCaught:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
TooGenericExceptionThrown:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
formatting:
|
||||||
|
Indentation:
|
||||||
|
indentSize: 4
|
||||||
|
|
||||||
|
NoWildcardImports:
|
||||||
|
active: false
|
||||||
|
|
||||||
|
naming:
|
||||||
|
FunctionMaxLength:
|
||||||
|
active: true
|
||||||
|
excludes:
|
||||||
|
- "**/test/**"
|
||||||
|
|
||||||
|
FunctionMinLength:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
LambdaParameterNaming:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
ConstructorParameterNaming:
|
||||||
|
excludes:
|
||||||
|
- "**/domain/model/ap/*"
|
||||||
|
ignoreOverridden: true
|
||||||
|
|
||||||
|
VariableNaming:
|
||||||
|
excludes:
|
||||||
|
- "**/domain/model/ap/*"
|
||||||
|
|
||||||
|
performance:
|
||||||
|
UnnecessaryPartOfBinaryExpression:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
UnnecessaryTemporaryInstantiation:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
potential-bugs:
|
||||||
|
CastToNullableType:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
DontDowncastCollectionTypes:
|
||||||
|
active: true
|
||||||
|
|
||||||
|
ElseCaseInsteadOfExhaustiveWhen:
|
||||||
|
active: true
|
|
@ -42,9 +42,9 @@ val Application.property: Application.(propertyName: String) -> String
|
||||||
environment.config.property(it).getString()
|
environment.config.property(it).getString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused") // application.conf references the main function. This annotation prevents the IDE from marking it as unused.
|
// application.conf references the main function. This annotation prevents the IDE from marking it as unused.
|
||||||
|
@Suppress("unused")
|
||||||
fun Application.parent() {
|
fun Application.parent() {
|
||||||
|
|
||||||
Config.configData = ConfigData(
|
Config.configData = ConfigData(
|
||||||
url = property("hideout.url"),
|
url = property("hideout.url"),
|
||||||
objectMapper = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
objectMapper = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||||
|
@ -62,12 +62,12 @@ fun Application.parent() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
single<IUserRepository> { UserRepository(get(),get()) }
|
single<IUserRepository> { UserRepository(get(), get()) }
|
||||||
single<IUserAuthService> { UserAuthService(get()) }
|
single<IUserAuthService> { UserAuthService(get()) }
|
||||||
single<HttpSignatureVerifyService> { HttpSignatureVerifyServiceImpl(get()) }
|
single<HttpSignatureVerifyService> { HttpSignatureVerifyServiceImpl(get()) }
|
||||||
single<JobQueueParentService> {
|
single<JobQueueParentService> {
|
||||||
val kJobJobQueueService = KJobJobQueueParentService(get())
|
val kJobJobQueueService = KJobJobQueueParentService(get())
|
||||||
kJobJobQueueService.init(listOf())
|
kJobJobQueueService.init(emptyList())
|
||||||
kJobJobQueueService
|
kJobJobQueueService
|
||||||
}
|
}
|
||||||
single<HttpClient> {
|
single<HttpClient> {
|
||||||
|
@ -83,15 +83,14 @@ fun Application.parent() {
|
||||||
}
|
}
|
||||||
single<ActivityPubFollowService> { ActivityPubFollowServiceImpl(get(), get(), get(), get()) }
|
single<ActivityPubFollowService> { ActivityPubFollowServiceImpl(get(), get(), get(), get()) }
|
||||||
single<ActivityPubService> { ActivityPubServiceImpl(get(), get()) }
|
single<ActivityPubService> { ActivityPubServiceImpl(get(), get()) }
|
||||||
single<IUserService> { UserService(get(),get()) }
|
single<IUserService> { UserService(get(), get()) }
|
||||||
single<ActivityPubUserService> { ActivityPubUserServiceImpl(get(), get()) }
|
single<ActivityPubUserService> { ActivityPubUserServiceImpl(get(), get()) }
|
||||||
single<ActivityPubNoteService> { ActivityPubNoteServiceImpl(get(), get(), get()) }
|
single<ActivityPubNoteService> { ActivityPubNoteServiceImpl(get(), get(), get()) }
|
||||||
single<IPostService> { PostService(get(), get()) }
|
single<IPostService> { PostService(get(), get()) }
|
||||||
single<IPostRepository> { PostRepositoryImpl(get(), get()) }
|
single<IPostRepository> { PostRepositoryImpl(get(), get()) }
|
||||||
single<IdGenerateService> {TwitterSnowflakeIdGenerateService}
|
single<IdGenerateService> { TwitterSnowflakeIdGenerateService }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
configureKoin(module)
|
configureKoin(module)
|
||||||
configureHTTP()
|
configureHTTP()
|
||||||
configureStaticRouting()
|
configureStaticRouting()
|
||||||
|
@ -120,7 +119,7 @@ fun Application.worker() {
|
||||||
activityPubService.processActivity(this, it)
|
activityPubService.processActivity(this, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kJob.register(DeliverPostJob){
|
kJob.register(DeliverPostJob) {
|
||||||
execute {
|
execute {
|
||||||
activityPubService.processActivity(this, it)
|
activityPubService.processActivity(this, it)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ open class Accept : Object {
|
||||||
name: String,
|
name: String,
|
||||||
`object`: Object?,
|
`object`: Object?,
|
||||||
actor: String?
|
actor: String?
|
||||||
) : super(add(type, "Accept"), name,actor) {
|
) : super(add(type, "Accept"), name, actor) {
|
||||||
this.`object` = `object`
|
this.`object` = `object`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,9 +29,5 @@ open class Accept : Object {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String = "Accept(`object`=$`object`, actor=$actor) ${super.toString()}"
|
||||||
return "Accept(`object`=$`object`, actor=$actor) ${super.toString()}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,5 @@ open class Create : Object {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String = "Create(`object`=$`object`) ${super.toString()}"
|
||||||
return "Create(`object`=$`object`) ${super.toString()}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,7 @@ open class Follow : Object {
|
||||||
name: String,
|
name: String,
|
||||||
`object`: String?,
|
`object`: String?,
|
||||||
actor: String?
|
actor: String?
|
||||||
) : super(add(type, "Follow"), name,actor) {
|
) : super(add(type, "Follow"), name, actor) {
|
||||||
this.`object` = `object`
|
this.`object` = `object`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,4 @@ open class Image : Object {
|
||||||
result = 31 * result + (url?.hashCode() ?: 0)
|
result = 31 * result + (url?.hashCode() ?: 0)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,15 +34,9 @@ open class JsonLd {
|
||||||
return context == other.context
|
return context == other.context
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int = context.hashCode()
|
||||||
return context.hashCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "JsonLd(context=$context)"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
override fun toString(): String = "JsonLd(context=$context)"
|
||||||
}
|
}
|
||||||
|
|
||||||
class ContextDeserializer : JsonDeserializer<String>() {
|
class ContextDeserializer : JsonDeserializer<String>() {
|
||||||
|
@ -60,10 +54,7 @@ class ContextDeserializer : JsonDeserializer<String>() {
|
||||||
|
|
||||||
class ContextSerializer : JsonSerializer<List<String>>() {
|
class ContextSerializer : JsonSerializer<List<String>>() {
|
||||||
|
|
||||||
|
override fun isEmpty(value: List<String>?): Boolean = value.isNullOrEmpty()
|
||||||
override fun isEmpty(value: List<String>?): Boolean {
|
|
||||||
return value.isNullOrEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun serialize(value: List<String>?, gen: JsonGenerator?, serializers: SerializerProvider?) {
|
override fun serialize(value: List<String>?, gen: JsonGenerator?, serializers: SerializerProvider?) {
|
||||||
if (value.isNullOrEmpty()) {
|
if (value.isNullOrEmpty()) {
|
||||||
|
@ -80,5 +71,4 @@ class ContextSerializer : JsonSerializer<List<String>>() {
|
||||||
gen?.writeEndArray()
|
gen?.writeEndArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ open class Key : Object {
|
||||||
id: String?,
|
id: String?,
|
||||||
owner: String?,
|
owner: String?,
|
||||||
publicKeyPem: String?
|
publicKeyPem: String?
|
||||||
) : super(add(type, "Key"), name,id) {
|
) : super(add(type, "Key"), name, id) {
|
||||||
this.owner = owner
|
this.owner = owner
|
||||||
this.publicKeyPem = publicKeyPem
|
this.publicKeyPem = publicKeyPem
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,4 @@ open class Key : Object {
|
||||||
result = 31 * result + (publicKeyPem?.hashCode() ?: 0)
|
result = 31 * result + (publicKeyPem?.hashCode() ?: 0)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,9 +48,6 @@ open class Note : Object {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String =
|
||||||
return "Note(id=$id, attributedTo=$attributedTo, content=$content, published=$published, to=$to) ${super.toString()}"
|
"Note(id=$id, attributedTo=$attributedTo, content=$content, published=$published, to=$to) ${super.toString()}"
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,27 +10,16 @@ open class Object : JsonLd {
|
||||||
private var type: List<String> = emptyList()
|
private var type: List<String> = emptyList()
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
var actor: String? = null
|
var actor: String? = null
|
||||||
var id:String? = null
|
var id: String? = null
|
||||||
|
|
||||||
protected constructor()
|
protected constructor()
|
||||||
constructor(type: List<String>, name: String? = null,actor:String? = null,id:String? = null) : super() {
|
constructor(type: List<String>, name: String? = null, actor: String? = null, id: String? = null) : super() {
|
||||||
this.type = type
|
this.type = type
|
||||||
this.name = name
|
this.name = name
|
||||||
this.actor = actor
|
this.actor = actor
|
||||||
this.id = id
|
this.id = id
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
protected fun add(list: List<String>, type: String): List<String> {
|
|
||||||
val toMutableList = list.toMutableList()
|
|
||||||
toMutableList.add(type)
|
|
||||||
return toMutableList.distinct()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other !is Object) return false
|
if (other !is Object) return false
|
||||||
|
@ -49,11 +38,16 @@ open class Object : JsonLd {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String = "Object(type=$type, name=$name, actor=$actor) ${super.toString()}"
|
||||||
return "Object(type=$type, name=$name, actor=$actor) ${super.toString()}"
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
protected fun add(list: List<String>, type: String): List<String> {
|
||||||
|
val toMutableList = list.toMutableList()
|
||||||
|
toMutableList.add(type)
|
||||||
|
return toMutableList.distinct()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class TypeSerializer : JsonSerializer<List<String>>() {
|
class TypeSerializer : JsonSerializer<List<String>>() {
|
||||||
|
@ -69,5 +63,4 @@ class TypeSerializer : JsonSerializer<List<String>>() {
|
||||||
gen?.writeEndArray()
|
gen?.writeEndArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ open class Person : Object {
|
||||||
var publicKey: Key? = null
|
var publicKey: Key? = null
|
||||||
|
|
||||||
protected constructor() : super()
|
protected constructor() : super()
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
constructor(
|
constructor(
|
||||||
type: List<String> = emptyList(),
|
type: List<String> = emptyList(),
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -21,7 +23,7 @@ open class Person : Object {
|
||||||
url: String?,
|
url: String?,
|
||||||
icon: Image?,
|
icon: Image?,
|
||||||
publicKey: Key?
|
publicKey: Key?
|
||||||
) : super(add(type, "Person"), name,id = id) {
|
) : super(add(type, "Person"), name, id = id) {
|
||||||
this.preferredUsername = preferredUsername
|
this.preferredUsername = preferredUsername
|
||||||
this.summary = summary
|
this.summary = summary
|
||||||
this.inbox = inbox
|
this.inbox = inbox
|
||||||
|
@ -56,6 +58,4 @@ open class Person : Object {
|
||||||
result = 31 * result + (publicKey?.hashCode() ?: 0)
|
result = 31 * result + (publicKey?.hashCode() ?: 0)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package dev.usbharu.hideout.domain.model.hideout.dto
|
package dev.usbharu.hideout.domain.model.hideout.dto
|
||||||
|
|
||||||
data class RemoteUserCreateDto(
|
data class RemoteUserCreateDto(
|
||||||
val name:String,
|
val name: String,
|
||||||
val domain:String,
|
val domain: String,
|
||||||
val screenName:String,
|
val screenName: String,
|
||||||
val description:String,
|
val description: String,
|
||||||
val inbox:String,
|
val inbox: String,
|
||||||
val outbox:String,
|
val outbox: String,
|
||||||
val url:String,
|
val url: String,
|
||||||
val publicKey:String,
|
val publicKey: String,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package dev.usbharu.hideout.domain.model.hideout.dto
|
package dev.usbharu.hideout.domain.model.hideout.dto
|
||||||
|
|
||||||
data class UserCreateDto(
|
data class UserCreateDto(
|
||||||
val name:String,
|
val name: String,
|
||||||
val screenName:String,
|
val screenName: String,
|
||||||
val description:String,
|
val description: String,
|
||||||
val password:String
|
val password: String
|
||||||
)
|
)
|
||||||
|
|
|
@ -61,9 +61,8 @@ val httpSignaturePlugin = createClientPlugin("HttpSign", ::HttpSignaturePluginCo
|
||||||
format.timeZone = TimeZone.getTimeZone("GMT")
|
format.timeZone = TimeZone.getTimeZone("GMT")
|
||||||
onRequest { request, body ->
|
onRequest { request, body ->
|
||||||
|
|
||||||
|
|
||||||
request.header("Date", format.format(Date()))
|
request.header("Date", format.format(Date()))
|
||||||
request.header("Host", "${request.url.host}")
|
request.header("Host", request.url.host)
|
||||||
println(request.bodyType)
|
println(request.bodyType)
|
||||||
println(request.bodyType?.type)
|
println(request.bodyType?.type)
|
||||||
if (request.bodyType?.type == String::class) {
|
if (request.bodyType?.type == String::class) {
|
||||||
|
@ -72,7 +71,7 @@ val httpSignaturePlugin = createClientPlugin("HttpSign", ::HttpSignaturePluginCo
|
||||||
// UserAuthService.sha256.reset()
|
// UserAuthService.sha256.reset()
|
||||||
val digest =
|
val digest =
|
||||||
Base64.getEncoder().encodeToString(UserAuthService.sha256.digest(body.toByteArray(Charsets.UTF_8)))
|
Base64.getEncoder().encodeToString(UserAuthService.sha256.digest(body.toByteArray(Charsets.UTF_8)))
|
||||||
request.headers.append("Digest", "sha-256=" + digest)
|
request.headers.append("Digest", "sha-256=$digest")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.headers.contains("Signature")) {
|
if (request.headers.contains("Signature")) {
|
||||||
|
@ -82,11 +81,21 @@ val httpSignaturePlugin = createClientPlugin("HttpSign", ::HttpSignaturePluginCo
|
||||||
s.split(",").forEach { parameters.add(it) }
|
s.split(",").forEach { parameters.add(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val keyId = parameters.find { it.startsWith("keyId") }?.split("=")?.get(1)?.replace("\"", "")
|
val keyId = parameters.find { it.startsWith("keyId") }
|
||||||
|
.orEmpty()
|
||||||
|
.split("=")[1]
|
||||||
|
.replace("\"", "")
|
||||||
val algorithm =
|
val algorithm =
|
||||||
parameters.find { it.startsWith("algorithm") }?.split("=")?.get(1)?.replace("\"", "")
|
parameters.find { it.startsWith("algorithm") }
|
||||||
val headers = parameters.find { it.startsWith("headers") }?.split("=")?.get(1)?.replace("\"", "")
|
.orEmpty()
|
||||||
?.split(" ")?.toMutableList().orEmpty()
|
.split("=")[1]
|
||||||
|
.replace("\"", "")
|
||||||
|
val headers = parameters.find { it.startsWith("headers") }
|
||||||
|
.orEmpty()
|
||||||
|
.split("=")[1]
|
||||||
|
.replace("\"", "")
|
||||||
|
.split(" ")
|
||||||
|
.toMutableList()
|
||||||
|
|
||||||
val algorithmType = when (algorithm) {
|
val algorithmType = when (algorithm) {
|
||||||
"rsa-sha256" -> {
|
"rsa-sha256" -> {
|
||||||
|
@ -132,32 +141,24 @@ val httpSignaturePlugin = createClientPlugin("HttpSign", ::HttpSignaturePluginCo
|
||||||
request.headers.remove("Signature")
|
request.headers.remove("Signature")
|
||||||
|
|
||||||
signer!!.sign(object : HttpMessage, HttpRequest {
|
signer!!.sign(object : HttpMessage, HttpRequest {
|
||||||
override fun headerValues(name: String?): MutableList<String> {
|
override fun headerValues(name: String?): MutableList<String> =
|
||||||
return name?.let { request.headers.getAll(it) }?.toMutableList() ?: mutableListOf()
|
name?.let { request.headers.getAll(it) }?.toMutableList() ?: mutableListOf()
|
||||||
}
|
|
||||||
|
|
||||||
override fun addHeader(name: String?, value: String?) {
|
override fun addHeader(name: String?, value: String?) {
|
||||||
val split = value?.split("=").orEmpty()
|
val split = value?.split("=").orEmpty()
|
||||||
name?.let { request.header(it, split.get(0) + "=\"" + split.get(1).trim('"') + "\"") }
|
name?.let { request.header(it, split.get(0) + "=\"" + split.get(1).trim('"') + "\"") }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun method(): String {
|
override fun method(): String = request.method.value
|
||||||
return request.method.value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun uri(): URI {
|
|
||||||
return request.url.build().toURI()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
override fun uri(): URI = request.url.build().toURI()
|
||||||
})
|
})
|
||||||
|
|
||||||
val signatureHeader = request.headers.getAll("Signature").orEmpty()
|
val signatureHeader = request.headers.getAll("Signature").orEmpty()
|
||||||
request.headers.remove("Signature")
|
request.headers.remove("Signature")
|
||||||
signatureHeader.map { it.replace("; ", ",").replace(";", ",") }.joinToString(",")
|
signatureHeader.joinToString(",") { it.replace("; ", ",").replace(";", ",") }
|
||||||
.let { request.header("Signature", it) }
|
.let { request.header("Signature", it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +168,8 @@ class KtorKeyMap(private val userAuthRepository: IUserRepository) : KeyMap {
|
||||||
.substringAfterLast("/")
|
.substringAfterLast("/")
|
||||||
val publicBytes = Base64.getDecoder().decode(
|
val publicBytes = Base64.getDecoder().decode(
|
||||||
userAuthRepository.findByNameAndDomain(
|
userAuthRepository.findByNameAndDomain(
|
||||||
username, Config.configData.domain
|
username,
|
||||||
|
Config.configData.domain
|
||||||
)?.publicKey?.replace("-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----")?.replace("", "")
|
)?.publicKey?.replace("-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----")?.replace("", "")
|
||||||
?.replace("\n", "")
|
?.replace("\n", "")
|
||||||
)
|
)
|
||||||
|
@ -180,7 +182,8 @@ class KtorKeyMap(private val userAuthRepository: IUserRepository) : KeyMap {
|
||||||
.substringAfterLast("/")
|
.substringAfterLast("/")
|
||||||
val publicBytes = Base64.getDecoder().decode(
|
val publicBytes = Base64.getDecoder().decode(
|
||||||
userAuthRepository.findByNameAndDomain(
|
userAuthRepository.findByNameAndDomain(
|
||||||
username, Config.configData.domain
|
username,
|
||||||
|
Config.configData.domain
|
||||||
)?.privateKey?.replace("-----BEGIN PRIVATE KEY-----", "")?.replace("-----END PRIVATE KEY-----", "")
|
)?.privateKey?.replace("-----BEGIN PRIVATE KEY-----", "")?.replace("-----END PRIVATE KEY-----", "")
|
||||||
?.replace("\n", "")
|
?.replace("\n", "")
|
||||||
)
|
)
|
||||||
|
@ -188,7 +191,6 @@ class KtorKeyMap(private val userAuthRepository: IUserRepository) : KeyMap {
|
||||||
return@runBlocking KeyFactory.getInstance("RSA").generatePrivate(x509EncodedKeySpec)
|
return@runBlocking KeyFactory.getInstance("RSA").generatePrivate(x509EncodedKeySpec)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSecretKey(keyId: String?): SecretKey {
|
@Suppress("NotImplementedDeclaration")
|
||||||
TODO("Not yet implemented")
|
override fun getSecretKey(keyId: String?): SecretKey = TODO("Not yet implemented")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,5 @@ fun Application.configureRouting(
|
||||||
route("/api/v1") {
|
route("/api/v1") {
|
||||||
statuses(postService)
|
statuses(postService)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
|
@file:Suppress("UnusedPrivateMember")
|
||||||
|
|
||||||
package dev.usbharu.hideout.plugins
|
package dev.usbharu.hideout.plugins
|
||||||
|
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
import dev.usbharu.hideout.service.IUserAuthService
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.auth.*
|
import io.ktor.server.auth.*
|
||||||
|
|
||||||
data class UserSession(val username: String) : Principal
|
const val TOKEN_AUTH = "token-auth"
|
||||||
|
|
||||||
const val tokenAuth = "token-auth"
|
|
||||||
|
|
||||||
fun Application.configureSecurity(userAuthService: IUserAuthService) {
|
fun Application.configureSecurity(userAuthService: IUserAuthService) {
|
||||||
install(Authentication) {
|
install(Authentication) {
|
||||||
bearer(tokenAuth) {
|
bearer(TOKEN_AUTH) {
|
||||||
authenticate { bearerTokenCredential ->
|
authenticate { bearerTokenCredential ->
|
||||||
UserIdPrincipal(bearerTokenCredential.token)
|
UserIdPrincipal(bearerTokenCredential.token)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import dev.usbharu.hideout.domain.model.Post
|
||||||
import dev.usbharu.hideout.domain.model.PostEntity
|
import dev.usbharu.hideout.domain.model.PostEntity
|
||||||
|
|
||||||
interface IPostRepository {
|
interface IPostRepository {
|
||||||
suspend fun insert(post:Post):PostEntity
|
suspend fun insert(post: Post): PostEntity
|
||||||
suspend fun findOneById(id:Long):PostEntity
|
suspend fun findOneById(id: Long): PostEntity
|
||||||
suspend fun delete(id:Long)
|
suspend fun delete(id: Long)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package dev.usbharu.hideout.repository
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
|
|
||||||
|
@Suppress("TooManyFunctions")
|
||||||
interface IUserRepository {
|
interface IUserRepository {
|
||||||
suspend fun save(user: User): User
|
suspend fun save(user: User): User
|
||||||
|
|
||||||
|
@ -13,11 +14,11 @@ interface IUserRepository {
|
||||||
|
|
||||||
suspend fun findByNameAndDomain(name: String, domain: String): User?
|
suspend fun findByNameAndDomain(name: String, domain: String): User?
|
||||||
|
|
||||||
suspend fun findByDomain(domain:String): List<User>
|
suspend fun findByDomain(domain: String): List<User>
|
||||||
|
|
||||||
suspend fun findByNameAndDomains(names: List<Pair<String,String>>): List<User>
|
suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<User>
|
||||||
|
|
||||||
suspend fun findByUrl(url:String): User?
|
suspend fun findByUrl(url: String): User?
|
||||||
|
|
||||||
suspend fun findByUrls(urls: List<String>): List<User>
|
suspend fun findByUrls(urls: List<String>): List<User>
|
||||||
|
|
||||||
|
@ -34,5 +35,5 @@ interface IUserRepository {
|
||||||
suspend fun deleteFollower(id: Long, follower: Long)
|
suspend fun deleteFollower(id: Long, follower: Long)
|
||||||
suspend fun findFollowersById(id: Long): List<User>
|
suspend fun findFollowersById(id: Long): List<User>
|
||||||
|
|
||||||
suspend fun nextId():Long
|
suspend fun nextId(): Long
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package dev.usbharu.hideout.repository
|
package dev.usbharu.hideout.repository
|
||||||
|
|
||||||
import dev.usbharu.hideout.config.Config
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.domain.model.*
|
import dev.usbharu.hideout.domain.model.Post
|
||||||
|
import dev.usbharu.hideout.domain.model.PostEntity
|
||||||
|
import dev.usbharu.hideout.domain.model.Posts
|
||||||
|
import dev.usbharu.hideout.domain.model.toPost
|
||||||
import dev.usbharu.hideout.service.IdGenerateService
|
import dev.usbharu.hideout.service.IdGenerateService
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
|
@ -22,7 +25,6 @@ class PostRepositoryImpl(database: Database, private val idGenerateService: IdGe
|
||||||
|
|
||||||
override suspend fun insert(post: Post): PostEntity {
|
override suspend fun insert(post: Post): PostEntity {
|
||||||
return query {
|
return query {
|
||||||
|
|
||||||
val generateId = idGenerateService.generateId()
|
val generateId = idGenerateService.generateId()
|
||||||
val name = Users.select { Users.id eq post.userId }.single().toUser().name
|
val name = Users.select { Users.id eq post.userId }.single().toUser().name
|
||||||
val postUrl = Config.configData.url + "/users/$name/posts/$generateId"
|
val postUrl = Config.configData.url + "/users/$name/posts/$generateId"
|
||||||
|
@ -38,15 +40,15 @@ class PostRepositoryImpl(database: Database, private val idGenerateService: IdGe
|
||||||
it[replyId] = post.replyId
|
it[replyId] = post.replyId
|
||||||
}
|
}
|
||||||
return@query PostEntity(
|
return@query PostEntity(
|
||||||
generateId,
|
id = generateId,
|
||||||
post.userId,
|
userId = post.userId,
|
||||||
post.overview,
|
overview = post.overview,
|
||||||
post.text,
|
text = post.text,
|
||||||
post.createdAt,
|
createdAt = post.createdAt,
|
||||||
post.visibility,
|
visibility = post.visibility,
|
||||||
postUrl,
|
url = postUrl,
|
||||||
post.repostId,
|
repostId = post.repostId,
|
||||||
post.replyId
|
replyId = post.replyId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,13 +136,13 @@ class UserRepository(private val database: Database, private val idGenerateServi
|
||||||
Users.innerJoin(
|
Users.innerJoin(
|
||||||
otherTable = UsersFollowers,
|
otherTable = UsersFollowers,
|
||||||
onColumn = { Users.id },
|
onColumn = { Users.id },
|
||||||
otherColumn = { userId })
|
otherColumn = { userId }
|
||||||
|
)
|
||||||
.innerJoin(
|
.innerJoin(
|
||||||
otherTable = followers,
|
otherTable = followers,
|
||||||
onColumn = { UsersFollowers.followerId },
|
onColumn = { UsersFollowers.followerId },
|
||||||
otherColumn = { followers[Users.id] })
|
otherColumn = { followers[Users.id] }
|
||||||
|
)
|
||||||
.slice(
|
.slice(
|
||||||
followers.get(Users.id),
|
followers.get(Users.id),
|
||||||
followers.get(Users.name),
|
followers.get(Users.name),
|
||||||
|
@ -177,7 +177,6 @@ class UserRepository(private val database: Database, private val idGenerateServi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override suspend fun delete(id: Long) {
|
override suspend fun delete(id: Long) {
|
||||||
query {
|
query {
|
||||||
Users.deleteWhere { Users.id.eq(id) }
|
Users.deleteWhere { Users.id.eq(id) }
|
||||||
|
@ -202,9 +201,7 @@ class UserRepository(private val database: Database, private val idGenerateServi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun nextId(): Long {
|
override suspend fun nextId(): Long = idGenerateService.generateId()
|
||||||
return idGenerateService.generateId()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Users : Table("users") {
|
object Users : Table("users") {
|
||||||
|
@ -230,18 +227,18 @@ object Users : Table("users") {
|
||||||
|
|
||||||
fun ResultRow.toUser(): User {
|
fun ResultRow.toUser(): User {
|
||||||
return User(
|
return User(
|
||||||
this[Users.id],
|
id = this[Users.id],
|
||||||
this[Users.name],
|
name = this[Users.name],
|
||||||
this[Users.domain],
|
domain = this[Users.domain],
|
||||||
this[Users.screenName],
|
screenName = this[Users.screenName],
|
||||||
this[Users.description],
|
description = this[Users.description],
|
||||||
this[Users.password],
|
password = this[Users.password],
|
||||||
this[Users.inbox],
|
inbox = this[Users.inbox],
|
||||||
this[Users.outbox],
|
outbox = this[Users.outbox],
|
||||||
this[Users.url],
|
url = this[Users.url],
|
||||||
this[Users.publicKey],
|
publicKey = this[Users.publicKey],
|
||||||
this[Users.privateKey],
|
privateKey = this[Users.privateKey],
|
||||||
Instant.ofEpochMilli((this[Users.createdAt]))
|
createdAt = Instant.ofEpochMilli((this[Users.createdAt]))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
fun Application.register(userService: IUserService) {
|
fun Application.register(userService: IUserService) {
|
||||||
|
|
||||||
routing {
|
routing {
|
||||||
get("/register") {
|
get("/register") {
|
||||||
val principal = call.principal<UserIdPrincipal>()
|
val principal = call.principal<UserIdPrincipal>()
|
||||||
|
|
|
@ -14,8 +14,7 @@ import io.ktor.server.routing.*
|
||||||
fun Routing.inbox(
|
fun Routing.inbox(
|
||||||
httpSignatureVerifyService: HttpSignatureVerifyService,
|
httpSignatureVerifyService: HttpSignatureVerifyService,
|
||||||
activityPubService: dev.usbharu.hideout.service.activitypub.ActivityPubService
|
activityPubService: dev.usbharu.hideout.service.activitypub.ActivityPubService
|
||||||
){
|
) {
|
||||||
|
|
||||||
route("/inbox") {
|
route("/inbox") {
|
||||||
get {
|
get {
|
||||||
call.respond(HttpStatusCode.MethodNotAllowed)
|
call.respond(HttpStatusCode.MethodNotAllowed)
|
||||||
|
@ -32,17 +31,20 @@ fun Routing.inbox(
|
||||||
when (response) {
|
when (response) {
|
||||||
is ActivityPubObjectResponse -> call.respond(
|
is ActivityPubObjectResponse -> call.respond(
|
||||||
response.httpStatusCode,
|
response.httpStatusCode,
|
||||||
Config.configData.objectMapper.writeValueAsString(response.message.apply {
|
Config.configData.objectMapper.writeValueAsString(
|
||||||
|
response.message.apply {
|
||||||
context =
|
context =
|
||||||
listOf("https://www.w3.org/ns/activitystreams")
|
listOf("https://www.w3.org/ns/activitystreams")
|
||||||
})
|
}
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
is ActivityPubStringResponse -> call.respond(response.httpStatusCode, response.message)
|
is ActivityPubStringResponse -> call.respond(response.httpStatusCode, response.message)
|
||||||
null -> call.respond(HttpStatusCode.NotImplemented)
|
null -> call.respond(HttpStatusCode.NotImplemented)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
route("/users/{name}/inbox"){
|
route("/users/{name}/inbox") {
|
||||||
get {
|
get {
|
||||||
call.respond(HttpStatusCode.MethodNotAllowed)
|
call.respond(HttpStatusCode.MethodNotAllowed)
|
||||||
}
|
}
|
||||||
|
@ -58,15 +60,17 @@ fun Routing.inbox(
|
||||||
when (response) {
|
when (response) {
|
||||||
is ActivityPubObjectResponse -> call.respond(
|
is ActivityPubObjectResponse -> call.respond(
|
||||||
response.httpStatusCode,
|
response.httpStatusCode,
|
||||||
Config.configData.objectMapper.writeValueAsString(response.message.apply {
|
Config.configData.objectMapper.writeValueAsString(
|
||||||
|
response.message.apply {
|
||||||
context =
|
context =
|
||||||
listOf("https://www.w3.org/ns/activitystreams")
|
listOf("https://www.w3.org/ns/activitystreams")
|
||||||
})
|
}
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
is ActivityPubStringResponse -> call.respond(response.httpStatusCode, response.message)
|
is ActivityPubStringResponse -> call.respond(response.httpStatusCode, response.message)
|
||||||
null -> call.respond(HttpStatusCode.NotImplemented)
|
null -> call.respond(HttpStatusCode.NotImplemented)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
fun Routing.outbox() {
|
fun Routing.outbox() {
|
||||||
|
|
||||||
route("/outbox") {
|
route("/outbox") {
|
||||||
get {
|
get {
|
||||||
call.respond(HttpStatusCode.NotImplemented)
|
call.respond(HttpStatusCode.NotImplemented)
|
||||||
|
@ -15,7 +14,7 @@ fun Routing.outbox() {
|
||||||
call.respond(HttpStatusCode.NotImplemented)
|
call.respond(HttpStatusCode.NotImplemented)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
route("/users/{name}/outbox"){
|
route("/users/{name}/outbox") {
|
||||||
get {
|
get {
|
||||||
call.respond(HttpStatusCode.NotImplemented)
|
call.respond(HttpStatusCode.NotImplemented)
|
||||||
}
|
}
|
||||||
|
@ -23,5 +22,4 @@ fun Routing.outbox() {
|
||||||
call.respond(HttpStatusCode.NotImplemented)
|
call.respond(HttpStatusCode.NotImplemented)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ fun Routing.usersAP(activityPubUserService: ActivityPubUserService, userService:
|
||||||
|
|
||||||
class ContentTypeRouteSelector(private vararg val contentType: ContentType) : RouteSelector() {
|
class ContentTypeRouteSelector(private vararg val contentType: ContentType) : RouteSelector() {
|
||||||
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation {
|
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation {
|
||||||
|
|
||||||
context.call.application.log.debug("Accept: ${context.call.request.accept()}")
|
context.call.application.log.debug("Accept: ${context.call.request.accept()}")
|
||||||
val requestContentType = context.call.request.accept() ?: return RouteSelectorEvaluation.FailedParameter
|
val requestContentType = context.call.request.accept() ?: return RouteSelectorEvaluation.FailedParameter
|
||||||
return if (requestContentType.split(",")
|
return if (requestContentType.split(",")
|
||||||
|
@ -45,5 +44,4 @@ class ContentTypeRouteSelector(private vararg val contentType: ContentType) : Ro
|
||||||
RouteSelectorEvaluation.FailedParameter
|
RouteSelectorEvaluation.FailedParameter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package dev.usbharu.hideout.routing.api.v1
|
||||||
import dev.usbharu.hideout.domain.model.Post
|
import dev.usbharu.hideout.domain.model.Post
|
||||||
import dev.usbharu.hideout.domain.model.api.StatusForPost
|
import dev.usbharu.hideout.domain.model.api.StatusForPost
|
||||||
import dev.usbharu.hideout.service.IPostService
|
import dev.usbharu.hideout.service.IPostService
|
||||||
import dev.usbharu.hideout.service.impl.PostService
|
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.request.*
|
import io.ktor.server.request.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
|
|
|
@ -11,8 +11,8 @@ import io.ktor.server.application.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
fun Routing.webfinger(userService: IUserService){
|
fun Routing.webfinger(userService: IUserService) {
|
||||||
route("/.well-known/webfinger"){
|
route("/.well-known/webfinger") {
|
||||||
get {
|
get {
|
||||||
val acct = call.request.queryParameters["resource"]?.decodeURLPart()
|
val acct = call.request.queryParameters["resource"]?.decodeURLPart()
|
||||||
?: throw ParameterNotExistException("Parameter(name='resource') does not exist.")
|
?: throw ParameterNotExistException("Parameter(name='resource') does not exist.")
|
||||||
|
|
|
@ -3,5 +3,5 @@ package dev.usbharu.hideout.service
|
||||||
import dev.usbharu.hideout.domain.model.Post
|
import dev.usbharu.hideout.domain.model.Post
|
||||||
|
|
||||||
interface IPostService {
|
interface IPostService {
|
||||||
suspend fun create(post:Post)
|
suspend fun create(post: Post)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,7 @@ interface IUserAuthService {
|
||||||
|
|
||||||
suspend fun usernameAlreadyUse(username: String): Boolean
|
suspend fun usernameAlreadyUse(username: String): Boolean
|
||||||
|
|
||||||
suspend fun generateKeyPair():KeyPair
|
suspend fun generateKeyPair(): KeyPair
|
||||||
|
|
||||||
suspend fun verifyAccount(username: String, password: String): Boolean
|
suspend fun verifyAccount(username: String, password: String): Boolean
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package dev.usbharu.hideout.service
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
interface IdGenerateService {
|
interface IdGenerateService {
|
||||||
suspend fun generateId():Long
|
suspend fun generateId(): Long
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
open class SnowflakeIdGenerateService(private val baseTime:Long) : IdGenerateService {
|
@Suppress("MagicNumber")
|
||||||
|
open class SnowflakeIdGenerateService(private val baseTime: Long) : IdGenerateService {
|
||||||
var lastTimeStamp: Long = -1
|
var lastTimeStamp: Long = -1
|
||||||
var sequenceId: Int = 0
|
var sequenceId: Int = 0
|
||||||
val mutex = Mutex()
|
val mutex = Mutex()
|
||||||
|
@ -13,22 +14,15 @@ open class SnowflakeIdGenerateService(private val baseTime:Long) : IdGenerateSer
|
||||||
@Throws(IllegalStateException::class)
|
@Throws(IllegalStateException::class)
|
||||||
override suspend fun generateId(): Long {
|
override suspend fun generateId(): Long {
|
||||||
return mutex.withLock {
|
return mutex.withLock {
|
||||||
|
|
||||||
var timestamp = getTime()
|
var timestamp = getTime()
|
||||||
if (timestamp < lastTimeStamp) {
|
if (timestamp < lastTimeStamp) {
|
||||||
while (timestamp <= lastTimeStamp) {
|
timestamp = wait(timestamp)
|
||||||
delay(1L)
|
|
||||||
timestamp = getTime()
|
|
||||||
}
|
|
||||||
// throw IllegalStateException(" $lastTimeStamp $timestamp ${lastTimeStamp-timestamp} ")
|
// throw IllegalStateException(" $lastTimeStamp $timestamp ${lastTimeStamp-timestamp} ")
|
||||||
}
|
}
|
||||||
if (timestamp == lastTimeStamp) {
|
if (timestamp == lastTimeStamp) {
|
||||||
sequenceId++
|
sequenceId++
|
||||||
if (sequenceId >= 4096) {
|
if (sequenceId >= 4096) {
|
||||||
while (timestamp <= lastTimeStamp) {
|
timestamp = wait(timestamp)
|
||||||
delay(1L)
|
|
||||||
timestamp = getTime()
|
|
||||||
}
|
|
||||||
sequenceId = 0
|
sequenceId = 0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -37,11 +31,16 @@ open class SnowflakeIdGenerateService(private val baseTime:Long) : IdGenerateSer
|
||||||
lastTimeStamp = timestamp
|
lastTimeStamp = timestamp
|
||||||
return@withLock (timestamp - baseTime).shl(22).or(1L.shl(12)).or(sequenceId.toLong())
|
return@withLock (timestamp - baseTime).shl(22).or(1L.shl(12)).or(sequenceId.toLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTime(): Long {
|
private suspend fun wait(timestamp: Long): Long {
|
||||||
return Instant.now().toEpochMilli()
|
var timestamp1 = timestamp
|
||||||
|
while (timestamp1 <= lastTimeStamp) {
|
||||||
|
delay(1L)
|
||||||
|
timestamp1 = getTime()
|
||||||
}
|
}
|
||||||
|
return timestamp1
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTime(): Long = Instant.now().toEpochMilli()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
package dev.usbharu.hideout.service
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
// 2010-11-04T01:42:54.657
|
// 2010-11-04T01:42:54.657
|
||||||
|
@Suppress("MagicNumber")
|
||||||
object TwitterSnowflakeIdGenerateService : SnowflakeIdGenerateService(1288834974657L)
|
object TwitterSnowflakeIdGenerateService : SnowflakeIdGenerateService(1288834974657L)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package dev.usbharu.hideout.service.activitypub
|
package dev.usbharu.hideout.service.activitypub
|
||||||
|
|
||||||
import dev.usbharu.hideout.domain.model.ap.Follow
|
|
||||||
import dev.usbharu.hideout.domain.model.ActivityPubResponse
|
import dev.usbharu.hideout.domain.model.ActivityPubResponse
|
||||||
|
import dev.usbharu.hideout.domain.model.ap.Follow
|
||||||
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
||||||
import kjob.core.job.JobProps
|
import kjob.core.job.JobProps
|
||||||
|
|
||||||
interface ActivityPubFollowService {
|
interface ActivityPubFollowService {
|
||||||
suspend fun receiveFollow(follow: Follow):ActivityPubResponse
|
suspend fun receiveFollow(follow: Follow): ActivityPubResponse
|
||||||
suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>)
|
suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ class ActivityPubFollowServiceImpl(
|
||||||
override suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>) {
|
override suspend fun receiveFollowJob(props: JobProps<ReceiveFollowJob>) {
|
||||||
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 = activityPubUserService.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"),
|
||||||
|
|
|
@ -6,6 +6,6 @@ import kjob.core.job.JobProps
|
||||||
|
|
||||||
interface ActivityPubNoteService {
|
interface ActivityPubNoteService {
|
||||||
|
|
||||||
suspend fun createNote(post:PostEntity)
|
suspend fun createNote(post: PostEntity)
|
||||||
suspend fun createNoteJob(props:JobProps<DeliverPostJob>)
|
suspend fun createNoteJob(props: JobProps<DeliverPostJob>)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ class ActivityPubNoteServiceImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override suspend fun createNoteJob(props: JobProps<DeliverPostJob>) {
|
override suspend fun createNoteJob(props: JobProps<DeliverPostJob>) {
|
||||||
val actor = props[DeliverPostJob.actor]
|
val actor = props[DeliverPostJob.actor]
|
||||||
val postEntity = Config.configData.objectMapper.readValue<PostEntity>(props[DeliverPostJob.post])
|
val postEntity = Config.configData.objectMapper.readValue<PostEntity>(props[DeliverPostJob.post])
|
||||||
|
|
|
@ -9,7 +9,7 @@ interface ActivityPubService {
|
||||||
|
|
||||||
suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse?
|
suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse?
|
||||||
|
|
||||||
suspend fun <T : HideoutJob> processActivity(job: JobContextWithProps<T>,hideoutJob: HideoutJob)
|
suspend fun <T : HideoutJob> processActivity(job: JobContextWithProps<T>, hideoutJob: HideoutJob)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ActivityType {
|
enum class ActivityType {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob
|
||||||
import dev.usbharu.hideout.exception.JsonParseException
|
import dev.usbharu.hideout.exception.JsonParseException
|
||||||
import kjob.core.dsl.JobContextWithProps
|
import kjob.core.dsl.JobContextWithProps
|
||||||
import kjob.core.job.JobProps
|
import kjob.core.job.JobProps
|
||||||
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
class ActivityPubServiceImpl(
|
class ActivityPubServiceImpl(
|
||||||
|
@ -17,7 +18,7 @@ class ActivityPubServiceImpl(
|
||||||
private val activityPubNoteService: ActivityPubNoteService
|
private val activityPubNoteService: ActivityPubNoteService
|
||||||
) : ActivityPubService {
|
) : ActivityPubService {
|
||||||
|
|
||||||
val logger = LoggerFactory.getLogger(this::class.java)
|
val logger: Logger = LoggerFactory.getLogger(this::class.java)
|
||||||
override fun parseActivity(json: String): ActivityType {
|
override fun parseActivity(json: String): ActivityType {
|
||||||
val readTree = Config.configData.objectMapper.readTree(json)
|
val readTree = Config.configData.objectMapper.readTree(json)
|
||||||
logger.debug("readTree: {}", readTree)
|
logger.debug("readTree: {}", readTree)
|
||||||
|
@ -33,6 +34,7 @@ class ActivityPubServiceImpl(
|
||||||
return ActivityType.values().first { it.name.equals(type.asText(), true) }
|
return ActivityType.values().first { it.name.equals(type.asText(), true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("CyclomaticComplexMethod", "NotImplementedDeclaration")
|
||||||
override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse {
|
override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
ActivityType.Accept -> TODO()
|
ActivityType.Accept -> TODO()
|
||||||
|
@ -80,6 +82,4 @@ class ActivityPubServiceImpl(
|
||||||
DeliverPostJob -> activityPubNoteService.createNoteJob(job.props as JobProps<DeliverPostJob>)
|
DeliverPostJob -> activityPubNoteService.createNoteJob(job.props as JobProps<DeliverPostJob>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package dev.usbharu.hideout.service.activitypub
|
||||||
import dev.usbharu.hideout.domain.model.ap.Person
|
import dev.usbharu.hideout.domain.model.ap.Person
|
||||||
|
|
||||||
interface ActivityPubUserService {
|
interface ActivityPubUserService {
|
||||||
suspend fun getPersonByName(name:String): Person
|
suspend fun getPersonByName(name: String): Person
|
||||||
|
|
||||||
suspend fun fetchPerson(url: String, targetActor: String? = null): Person
|
suspend fun fetchPerson(url: String, targetActor: String? = null): Person
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import io.ktor.client.*
|
||||||
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.*
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
|
|
||||||
class ActivityPubUserServiceImpl(
|
class ActivityPubUserServiceImpl(
|
||||||
private val userService: IUserService,
|
private val userService: IUserService,
|
||||||
|
@ -23,7 +22,6 @@ class ActivityPubUserServiceImpl(
|
||||||
) :
|
) :
|
||||||
ActivityPubUserService {
|
ActivityPubUserService {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
|
||||||
override suspend fun getPersonByName(name: String): Person {
|
override suspend fun getPersonByName(name: String): Person {
|
||||||
// TODO: JOINで書き直し
|
// TODO: JOINで書き直し
|
||||||
val userEntity = userService.findByNameLocalUser(name)
|
val userEntity = userService.findByNameLocalUser(name)
|
||||||
|
@ -79,11 +77,10 @@ class ActivityPubUserServiceImpl(
|
||||||
publicKeyPem = userEntity.publicKey
|
publicKeyPem = userEntity.publicKey
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
} catch (e: UserNotFoundException) {
|
} catch (e: UserNotFoundException) {
|
||||||
val httpResponse = if (targetActor != null) {
|
val httpResponse = if (targetActor != null) {
|
||||||
httpClient.getAp(url,"$targetActor#pubkey")
|
httpClient.getAp(url, "$targetActor#pubkey")
|
||||||
}else {
|
} else {
|
||||||
httpClient.get(url) {
|
httpClient.get(url) {
|
||||||
accept(ContentType.Application.Activity)
|
accept(ContentType.Application.Activity)
|
||||||
}
|
}
|
||||||
|
@ -95,16 +92,17 @@ class ActivityPubUserServiceImpl(
|
||||||
name = person.preferredUsername
|
name = person.preferredUsername
|
||||||
?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
||||||
domain = url.substringAfter("://").substringBeforeLast("/"),
|
domain = url.substringAfter("://").substringBeforeLast("/"),
|
||||||
screenName = (person.name ?: person.preferredUsername) ?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
screenName = (person.name ?: person.preferredUsername)
|
||||||
description = person.summary ?: "",
|
?: throw IllegalActivityPubObjectException("preferredUsername is null"),
|
||||||
|
description = person.summary.orEmpty(),
|
||||||
inbox = person.inbox ?: throw IllegalActivityPubObjectException("inbox is null"),
|
inbox = person.inbox ?: throw IllegalActivityPubObjectException("inbox is null"),
|
||||||
outbox = person.outbox ?: throw IllegalActivityPubObjectException("outbox is null"),
|
outbox = person.outbox ?: throw IllegalActivityPubObjectException("outbox is null"),
|
||||||
url = url,
|
url = url,
|
||||||
publicKey = person.publicKey?.publicKeyPem ?: throw IllegalActivityPubObjectException("publicKey is null"),
|
publicKey = person.publicKey?.publicKeyPem
|
||||||
|
?: throw IllegalActivityPubObjectException("publicKey is null"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
person
|
person
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ 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.domain.model.hideout.entity.User
|
import dev.usbharu.hideout.domain.model.hideout.entity.User
|
||||||
|
|
||||||
|
@Suppress("TooManyFunctions")
|
||||||
interface IUserService {
|
interface IUserService {
|
||||||
suspend fun findAll(limit: Int? = 100, offset: Long? = 0): List<User>
|
suspend fun findAll(limit: Int? = 100, offset: Long? = 0): List<User>
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,17 @@ import dev.usbharu.hideout.domain.model.Post
|
||||||
import dev.usbharu.hideout.repository.IPostRepository
|
import dev.usbharu.hideout.repository.IPostRepository
|
||||||
import dev.usbharu.hideout.service.IPostService
|
import dev.usbharu.hideout.service.IPostService
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubNoteService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubNoteService
|
||||||
import dev.usbharu.hideout.service.job.JobQueueParentService
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
class PostService(private val postRepository:IPostRepository,private val activityPubNoteService: ActivityPubNoteService) : IPostService {
|
class PostService(
|
||||||
|
private val postRepository: IPostRepository,
|
||||||
|
private val activityPubNoteService: ActivityPubNoteService
|
||||||
|
) : IPostService {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
override suspend fun create(post: Post) {
|
override suspend fun create(post: Post) {
|
||||||
logger.debug("create post={}",post)
|
logger.debug("create post={}", post)
|
||||||
val postEntity = postRepository.insert(post)
|
val postEntity = postRepository.insert(post)
|
||||||
activityPubNoteService.createNote(postEntity)
|
activityPubNoteService.createNote(postEntity)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,13 @@ class UserAuthService(
|
||||||
val userRepository: IUserRepository
|
val userRepository: IUserRepository
|
||||||
) : IUserAuthService {
|
) : IUserAuthService {
|
||||||
|
|
||||||
|
|
||||||
override fun hash(password: String): String {
|
override fun hash(password: String): String {
|
||||||
val digest = sha256.digest(password.toByteArray(Charsets.UTF_8))
|
val digest = sha256.digest(password.toByteArray(Charsets.UTF_8))
|
||||||
return hex(digest)
|
return hex(digest)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun usernameAlreadyUse(username: String): Boolean {
|
override suspend fun usernameAlreadyUse(username: String): Boolean {
|
||||||
userRepository.findByName(username) ?: return false
|
userRepository.findByName(username)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,24 +30,25 @@ class UserAuthService(
|
||||||
|
|
||||||
override suspend fun generateKeyPair(): KeyPair {
|
override suspend fun generateKeyPair(): KeyPair {
|
||||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||||
keyPairGenerator.initialize(2048)
|
keyPairGenerator.initialize(keySize)
|
||||||
return keyPairGenerator.generateKeyPair()
|
return keyPairGenerator.generateKeyPair()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val sha256: MessageDigest = MessageDigest.getInstance("SHA-256")
|
val sha256: MessageDigest = MessageDigest.getInstance("SHA-256")
|
||||||
|
const val keySize = 2048
|
||||||
|
const val pemSize = 64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PublicKey.toPem(): String {
|
fun PublicKey.toPem(): String {
|
||||||
return "-----BEGIN PUBLIC KEY-----\n" +
|
return "-----BEGIN PUBLIC KEY-----\n" +
|
||||||
Base64.getEncoder().encodeToString(encoded).chunked(64).joinToString("\n") +
|
Base64.getEncoder().encodeToString(encoded).chunked(UserAuthService.pemSize).joinToString("\n") +
|
||||||
"\n-----END PUBLIC KEY-----\n"
|
"\n-----END PUBLIC KEY-----\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PrivateKey.toPem(): String {
|
fun PrivateKey.toPem(): String {
|
||||||
return "-----BEGIN PRIVATE KEY-----\n" +
|
return "-----BEGIN PRIVATE KEY-----\n" +
|
||||||
Base64.getEncoder().encodeToString(encoded).chunked(64).joinToString("\n") +
|
Base64.getEncoder().encodeToString(encoded).chunked(UserAuthService.pemSize).joinToString("\n") +
|
||||||
"\n-----END PRIVATE KEY-----\n"
|
"\n-----END PRIVATE KEY-----\n"
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,41 +15,31 @@ class UserService(private val userRepository: IUserRepository, private val userA
|
||||||
|
|
||||||
private val maxLimit = 100
|
private val maxLimit = 100
|
||||||
override suspend fun findAll(limit: Int?, offset: Long?): List<User> {
|
override suspend fun findAll(limit: Int?, offset: Long?): List<User> {
|
||||||
|
|
||||||
return userRepository.findAllByLimitAndByOffset(
|
return userRepository.findAllByLimitAndByOffset(
|
||||||
min(limit ?: maxLimit, maxLimit),
|
min(limit ?: maxLimit, maxLimit),
|
||||||
offset ?: 0
|
offset ?: 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findById(id: Long): User {
|
override suspend fun findById(id: Long): User =
|
||||||
return userRepository.findById(id) ?: throw UserNotFoundException("$id was not found.")
|
userRepository.findById(id) ?: throw UserNotFoundException("$id was not found.")
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByIds(ids: List<Long>): List<User> {
|
override suspend fun findByIds(ids: List<Long>): List<User> = userRepository.findByIds(ids)
|
||||||
return userRepository.findByIds(ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByName(name: String): List<User> {
|
override suspend fun findByName(name: String): List<User> = userRepository.findByName(name)
|
||||||
return userRepository.findByName(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByNameLocalUser(name: String): User {
|
override suspend fun findByNameLocalUser(name: String): User {
|
||||||
return userRepository.findByNameAndDomain(name, Config.configData.domain)
|
return userRepository.findByNameAndDomain(name, Config.configData.domain)
|
||||||
?: throw UserNotFoundException("$name was not found.")
|
?: throw UserNotFoundException("$name was not found.")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<User> {
|
override suspend fun findByNameAndDomains(names: List<Pair<String, String>>): List<User> =
|
||||||
return userRepository.findByNameAndDomains(names)
|
userRepository.findByNameAndDomains(names)
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByUrl(url: String): User {
|
override suspend fun findByUrl(url: String): User =
|
||||||
return userRepository.findByUrl(url) ?: throw UserNotFoundException("$url was not found.")
|
userRepository.findByUrl(url) ?: throw UserNotFoundException("$url was not found.")
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun findByUrls(urls: List<String>): List<User> {
|
override suspend fun findByUrls(urls: List<String>): List<User> = userRepository.findByUrls(urls)
|
||||||
return userRepository.findByUrls(urls)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun usernameAlreadyUse(username: String): Boolean {
|
override suspend fun usernameAlreadyUse(username: String): Boolean {
|
||||||
val findByNameAndDomain = userRepository.findByNameAndDomain(username, Config.configData.domain)
|
val findByNameAndDomain = userRepository.findByNameAndDomain(username, Config.configData.domain)
|
||||||
|
@ -58,7 +48,7 @@ class UserService(private val userRepository: IUserRepository, private val userA
|
||||||
|
|
||||||
override suspend fun createLocalUser(user: UserCreateDto): User {
|
override suspend fun createLocalUser(user: UserCreateDto): User {
|
||||||
val nextId = userRepository.nextId()
|
val nextId = userRepository.nextId()
|
||||||
val HashedPassword = userAuthService.hash(user.password)
|
val hashedPassword = userAuthService.hash(user.password)
|
||||||
val keyPair = userAuthService.generateKeyPair()
|
val keyPair = userAuthService.generateKeyPair()
|
||||||
val userEntity = User(
|
val userEntity = User(
|
||||||
id = nextId,
|
id = nextId,
|
||||||
|
@ -66,13 +56,13 @@ class UserService(private val userRepository: IUserRepository, private val userA
|
||||||
domain = Config.configData.domain,
|
domain = Config.configData.domain,
|
||||||
screenName = user.screenName,
|
screenName = user.screenName,
|
||||||
description = user.description,
|
description = user.description,
|
||||||
password = HashedPassword,
|
password = hashedPassword,
|
||||||
inbox = "${Config.configData.url}/users/${user.name}/inbox",
|
inbox = "${Config.configData.url}/users/${user.name}/inbox",
|
||||||
outbox = "${Config.configData.url}/users/${user.name}/outbox",
|
outbox = "${Config.configData.url}/users/${user.name}/outbox",
|
||||||
url = "${Config.configData.url}/users/${user.name}",
|
url = "${Config.configData.url}/users/${user.name}",
|
||||||
publicKey = keyPair.public.toPem(),
|
publicKey = keyPair.public.toPem(),
|
||||||
privateKey = keyPair.private.toPem(),
|
privateKey = keyPair.private.toPem(),
|
||||||
Instant.now()
|
createdAt = Instant.now()
|
||||||
)
|
)
|
||||||
return userRepository.save(userEntity)
|
return userRepository.save(userEntity)
|
||||||
}
|
}
|
||||||
|
@ -94,12 +84,7 @@ class UserService(private val userRepository: IUserRepository, private val userA
|
||||||
return userRepository.save(userEntity)
|
return userRepository.save(userEntity)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findFollowersById(id: Long): List<User> {
|
override suspend fun findFollowersById(id: Long): List<User> = userRepository.findFollowersById(id)
|
||||||
return userRepository.findFollowersById(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun addFollowers(id: Long, follower: Long) {
|
|
||||||
return userRepository.createFollower(id, follower)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
override suspend fun addFollowers(id: Long, follower: Long) = userRepository.createFollower(id, follower)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,6 @@ import kjob.core.dsl.ScheduleContext
|
||||||
|
|
||||||
interface JobQueueParentService {
|
interface JobQueueParentService {
|
||||||
|
|
||||||
fun init(jobDefines:List<Job>)
|
fun init(jobDefines: List<Job>)
|
||||||
suspend fun <J : Job> schedule(job: J, block: ScheduleContext<J>.(J) -> Unit = {})
|
suspend fun <J : Job> schedule(job: J, block: ScheduleContext<J>.(J) -> Unit = {})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package dev.usbharu.hideout.service.job
|
package dev.usbharu.hideout.service.job
|
||||||
|
|
||||||
import kjob.core.Job
|
import kjob.core.Job
|
||||||
import kjob.core.dsl.JobContextWithProps
|
|
||||||
import kjob.core.dsl.JobRegisterContext
|
|
||||||
import kjob.core.dsl.KJobFunctions
|
import kjob.core.dsl.KJobFunctions
|
||||||
|
import kjob.core.dsl.JobContextWithProps as JCWP
|
||||||
|
import kjob.core.dsl.JobRegisterContext as JRC
|
||||||
|
|
||||||
interface JobQueueWorkerService {
|
interface JobQueueWorkerService {
|
||||||
fun init(defines: List<Pair<Job, JobRegisterContext<Job, JobContextWithProps<Job>>.(Job) -> KJobFunctions<Job, JobContextWithProps<Job>>>>)
|
fun init(defines: List<Pair<Job, JRC<Job, JCWP<Job>>.(Job) -> KJobFunctions<Job, JCWP<Job>>>>)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,7 @@ class KJobJobQueueParentService(private val database: Database) : JobQueueParent
|
||||||
isWorker = false
|
isWorker = false
|
||||||
}.start()
|
}.start()
|
||||||
|
|
||||||
override fun init(jobDefines: List<Job>) {
|
override fun init(jobDefines: List<Job>) = Unit
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun <J : Job> schedule(job: J, block: ScheduleContext<J>.(J) -> Unit) {
|
override suspend fun <J : Job> schedule(job: J, block: ScheduleContext<J>.(J) -> Unit) {
|
||||||
logger.debug("schedule job={}", job.name)
|
logger.debug("schedule job={}", job.name)
|
||||||
|
|
|
@ -2,11 +2,11 @@ package dev.usbharu.hideout.service.job
|
||||||
|
|
||||||
import dev.usbharu.kjob.exposed.ExposedKJob
|
import dev.usbharu.kjob.exposed.ExposedKJob
|
||||||
import kjob.core.Job
|
import kjob.core.Job
|
||||||
import kjob.core.dsl.JobContextWithProps
|
|
||||||
import kjob.core.dsl.JobRegisterContext
|
|
||||||
import kjob.core.dsl.KJobFunctions
|
import kjob.core.dsl.KJobFunctions
|
||||||
import kjob.core.kjob
|
import kjob.core.kjob
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.sql.Database
|
||||||
|
import kjob.core.dsl.JobContextWithProps as JCWP
|
||||||
|
import kjob.core.dsl.JobRegisterContext as JRC
|
||||||
|
|
||||||
class KJobJobQueueWorkerService(private val database: Database) : JobQueueWorkerService {
|
class KJobJobQueueWorkerService(private val database: Database) : JobQueueWorkerService {
|
||||||
|
|
||||||
|
@ -19,10 +19,11 @@ class KJobJobQueueWorkerService(private val database: Database) : JobQueueWorker
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun init(defines: List<Pair<Job,JobRegisterContext<Job, JobContextWithProps<Job>>.(Job) -> KJobFunctions<Job, JobContextWithProps<Job>>>>) {
|
override fun init(
|
||||||
|
defines: List<Pair<Job, JRC<Job, JCWP<Job>>.(Job) -> KJobFunctions<Job, JCWP<Job>>>>
|
||||||
|
) {
|
||||||
defines.forEach { job ->
|
defines.forEach { job ->
|
||||||
kjob.register(job.first, job.second)
|
kjob.register(job.first, job.second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,5 @@ package dev.usbharu.hideout.service.signature
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
|
||||||
interface HttpSignatureVerifyService {
|
interface HttpSignatureVerifyService {
|
||||||
fun verify(headers:Headers):Boolean
|
fun verify(headers: Headers): Boolean
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,22 @@ package dev.usbharu.hideout.service.signature
|
||||||
|
|
||||||
import dev.usbharu.hideout.plugins.KtorKeyMap
|
import dev.usbharu.hideout.plugins.KtorKeyMap
|
||||||
import dev.usbharu.hideout.repository.IUserRepository
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
import dev.usbharu.hideout.service.IUserAuthService
|
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import tech.barbero.http.message.signing.HttpMessage
|
|
||||||
import tech.barbero.http.message.signing.SignatureHeaderVerifier
|
import tech.barbero.http.message.signing.SignatureHeaderVerifier
|
||||||
|
|
||||||
class HttpSignatureVerifyServiceImpl(private val userAuthService: IUserRepository) : HttpSignatureVerifyService {
|
class HttpSignatureVerifyServiceImpl(private val userAuthService: IUserRepository) : HttpSignatureVerifyService {
|
||||||
override fun verify(headers: Headers): Boolean {
|
override fun verify(headers: Headers): Boolean {
|
||||||
val build = SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userAuthService)).build()
|
val build = SignatureHeaderVerifier.builder().keyMap(KtorKeyMap(userAuthService)).build()
|
||||||
return true
|
return true
|
||||||
build.verify(object : HttpMessage {
|
// build.verify(object : HttpMessage {
|
||||||
override fun headerValues(name: String?): MutableList<String> {
|
// override fun headerValues(name: String?): MutableList<String> {
|
||||||
return name?.let { headers.getAll(it) }?.toMutableList() ?: mutableListOf()
|
// return name?.let { headers.getAll(it) }?.toMutableList() ?: mutableListOf()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
override fun addHeader(name: String?, value: String?) {
|
// override fun addHeader(name: String?, value: String?) {
|
||||||
TODO()
|
// TODO()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
})
|
// })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,18 @@ package dev.usbharu.hideout.util
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
|
||||||
object HttpUtil {
|
object HttpUtil {
|
||||||
|
val ContentType.Application.Activity: ContentType
|
||||||
|
get() = ContentType("application", "activity+json")
|
||||||
|
|
||||||
|
val ContentType.Application.JsonLd: ContentType
|
||||||
|
get() {
|
||||||
|
return ContentType(
|
||||||
|
contentType = "application",
|
||||||
|
contentSubtype = "ld+json",
|
||||||
|
parameters = listOf(HeaderValueParam("profile", "https://www.w3.org/ns/activitystreams"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun isContentTypeOfActivityPub(
|
fun isContentTypeOfActivityPub(
|
||||||
contentType: String,
|
contentType: String,
|
||||||
subType: String,
|
subType: String,
|
||||||
|
@ -25,15 +37,5 @@ object HttpUtil {
|
||||||
contentType.parameter("profile").orEmpty()
|
contentType.parameter("profile").orEmpty()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val ContentType.Application.Activity: ContentType
|
|
||||||
get() = ContentType("application", "activity+json")
|
|
||||||
|
|
||||||
val ContentType.Application.JsonLd: ContentType
|
|
||||||
get() = ContentType(
|
|
||||||
"application",
|
|
||||||
"ld+json",
|
|
||||||
listOf(HeaderValueParam("profile", "https://www.w3.org/ns/activitystreams"))
|
|
||||||
)
|
|
||||||
// fun
|
// fun
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,6 @@ class ExposedJobRepository(
|
||||||
|
|
||||||
suspend fun <T> query(block: suspend () -> T): T = newSuspendedTransaction(Dispatchers.IO) { block() }
|
suspend fun <T> query(block: suspend () -> T): T = newSuspendedTransaction(Dispatchers.IO) { block() }
|
||||||
|
|
||||||
|
|
||||||
override suspend fun completeProgress(id: String): Boolean {
|
override suspend fun completeProgress(id: String): Boolean {
|
||||||
val now = Instant.now(clock).toEpochMilli()
|
val now = Instant.now(clock).toEpochMilli()
|
||||||
return query {
|
return query {
|
||||||
|
@ -86,12 +85,17 @@ class ExposedJobRepository(
|
||||||
override suspend fun get(id: String): ScheduledJob? {
|
override suspend fun get(id: String): ScheduledJob? {
|
||||||
val single = query { jobs.select(jobs.id eq id.toLong()).singleOrNull() } ?: return null
|
val single = query { jobs.select(jobs.id eq id.toLong()).singleOrNull() } ?: return null
|
||||||
return single.toScheduledJob()
|
return single.toScheduledJob()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun reset(id: String, oldKjobId: UUID?): Boolean {
|
override suspend fun reset(id: String, oldKjobId: UUID?): Boolean {
|
||||||
return query {
|
return query {
|
||||||
jobs.update({ jobs.id eq id.toLong() and if (oldKjobId == null) jobs.kjobId.isNull() else jobs.kjobId eq oldKjobId.toString() }) {
|
jobs.update({
|
||||||
|
jobs.id eq id.toLong() and if (oldKjobId == null) {
|
||||||
|
jobs.kjobId.isNull()
|
||||||
|
} else {
|
||||||
|
jobs.kjobId eq oldKjobId.toString()
|
||||||
|
}
|
||||||
|
}) {
|
||||||
it[jobs.status] = JobStatus.CREATED.name
|
it[jobs.status] = JobStatus.CREATED.name
|
||||||
it[jobs.statusMessage] = null
|
it[jobs.statusMessage] = null
|
||||||
it[jobs.kjobId] = null
|
it[jobs.kjobId] = null
|
||||||
|
@ -107,7 +111,18 @@ class ExposedJobRepository(
|
||||||
override suspend fun save(jobSettings: JobSettings, runAt: Instant?): ScheduledJob {
|
override suspend fun save(jobSettings: JobSettings, runAt: Instant?): ScheduledJob {
|
||||||
val now = Instant.now(clock)
|
val now = Instant.now(clock)
|
||||||
val scheduledJob =
|
val scheduledJob =
|
||||||
ScheduledJob("", JobStatus.CREATED, runAt, null, 0, null, now, now, jobSettings, JobProgress(0))
|
ScheduledJob(
|
||||||
|
id = "",
|
||||||
|
status = JobStatus.CREATED,
|
||||||
|
runAt = runAt,
|
||||||
|
statusMessage = null,
|
||||||
|
retries = 0,
|
||||||
|
kjobId = null,
|
||||||
|
createdAt = now,
|
||||||
|
updatedAt = now,
|
||||||
|
settings = jobSettings,
|
||||||
|
progress = JobProgress(0)
|
||||||
|
)
|
||||||
val id = query {
|
val id = query {
|
||||||
jobs.insert {
|
jobs.insert {
|
||||||
it[jobs.status] = scheduledJob.status.name
|
it[jobs.status] = scheduledJob.status.name
|
||||||
|
@ -168,7 +183,13 @@ class ExposedJobRepository(
|
||||||
retries: Int
|
retries: Int
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return query {
|
return query {
|
||||||
jobs.update({ (jobs.id eq id.toLong()) and if (oldKjobId == null) jobs.kjobId.isNull() else jobs.kjobId eq oldKjobId.toString() }) {
|
jobs.update({
|
||||||
|
(jobs.id eq id.toLong()) and if (oldKjobId == null) {
|
||||||
|
jobs.kjobId.isNull()
|
||||||
|
} else {
|
||||||
|
jobs.kjobId eq oldKjobId.toString()
|
||||||
|
}
|
||||||
|
}) {
|
||||||
it[jobs.status] = status.name
|
it[jobs.status] = status.name
|
||||||
it[jobs.retries] = retries
|
it[jobs.retries] = retries
|
||||||
it[jobs.updatedAt] = Instant.now(clock).toEpochMilli()
|
it[jobs.updatedAt] = Instant.now(clock).toEpochMilli()
|
||||||
|
@ -214,6 +235,7 @@ class ExposedJobRepository(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun listSerialize(value: List<*>): JsonElement {
|
fun listSerialize(value: List<*>): JsonElement {
|
||||||
return if (value.isEmpty()) {
|
return if (value.isEmpty()) {
|
||||||
buildJsonObject {
|
buildJsonObject {
|
||||||
|
@ -227,7 +249,7 @@ class ExposedJobRepository(
|
||||||
is Int -> "i" to (value as List<Int>).map(::JsonPrimitive)
|
is Int -> "i" to (value as List<Int>).map(::JsonPrimitive)
|
||||||
is String -> "s" to (value as List<String>).map(::JsonPrimitive)
|
is String -> "s" to (value as List<String>).map(::JsonPrimitive)
|
||||||
is Boolean -> "b" to (value as List<Boolean>).map(::JsonPrimitive)
|
is Boolean -> "b" to (value as List<Boolean>).map(::JsonPrimitive)
|
||||||
else -> error("Cannot serialize unsupported list property value: $value")
|
else -> error("Cannot serialize unsupported list property value: $item")
|
||||||
}
|
}
|
||||||
buildJsonObject {
|
buildJsonObject {
|
||||||
put("t", t)
|
put("t", t)
|
||||||
|
@ -265,6 +287,7 @@ class ExposedJobRepository(
|
||||||
retries = single[retries],
|
retries = single[retries],
|
||||||
kjobId = single[kjobId]?.let {
|
kjobId = single[kjobId]?.let {
|
||||||
try {
|
try {
|
||||||
|
@Suppress("SwallowedException")
|
||||||
UUID.fromString(it)
|
UUID.fromString(it)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
null
|
null
|
||||||
|
|
|
@ -9,24 +9,6 @@ import java.time.Clock
|
||||||
|
|
||||||
class ExposedKJob(config: Configuration) : BaseKJob<ExposedKJob.Configuration>(config) {
|
class ExposedKJob(config: Configuration) : BaseKJob<ExposedKJob.Configuration>(config) {
|
||||||
|
|
||||||
companion object : KJobFactory<ExposedKJob, Configuration> {
|
|
||||||
override fun create(configure: Configuration.() -> Unit): KJob {
|
|
||||||
return ExposedKJob(Configuration().apply(configure))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Configuration : BaseKJob.Configuration() {
|
|
||||||
var connectionString: String? = null
|
|
||||||
var driverClassName: String? = null
|
|
||||||
var connectionDatabase: Database? = null
|
|
||||||
|
|
||||||
var jobTableName = "kjobJobs"
|
|
||||||
|
|
||||||
var lockTableName = "kjobLocks"
|
|
||||||
|
|
||||||
var expireLockInMinutes = 5L
|
|
||||||
}
|
|
||||||
|
|
||||||
private val database: Database = config.connectionDatabase ?: Database.connect(
|
private val database: Database = config.connectionDatabase ?: Database.connect(
|
||||||
requireNotNull(config.connectionString),
|
requireNotNull(config.connectionString),
|
||||||
requireNotNull(config.driverClassName)
|
requireNotNull(config.driverClassName)
|
||||||
|
@ -34,6 +16,7 @@ class ExposedKJob(config: Configuration) : BaseKJob<ExposedKJob.Configuration>(c
|
||||||
|
|
||||||
override val jobRepository: ExposedJobRepository
|
override val jobRepository: ExposedJobRepository
|
||||||
get() = ExposedJobRepository(database, config.jobTableName, Clock.systemUTC(), config.json)
|
get() = ExposedJobRepository(database, config.jobTableName, Clock.systemUTC(), config.json)
|
||||||
|
|
||||||
override val lockRepository: ExposedLockRepository
|
override val lockRepository: ExposedLockRepository
|
||||||
get() = ExposedLockRepository(database, config, clock)
|
get() = ExposedLockRepository(database, config, clock)
|
||||||
|
|
||||||
|
@ -47,4 +30,20 @@ class ExposedKJob(config: Configuration) : BaseKJob<ExposedKJob.Configuration>(c
|
||||||
super.shutdown()
|
super.shutdown()
|
||||||
lockRepository.clearExpired()
|
lockRepository.clearExpired()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object : KJobFactory<ExposedKJob, Configuration> {
|
||||||
|
override fun create(configure: Configuration.() -> Unit): KJob = ExposedKJob(Configuration().apply(configure))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Configuration : BaseKJob.Configuration() {
|
||||||
|
var connectionString: String? = null
|
||||||
|
var driverClassName: String? = null
|
||||||
|
var connectionDatabase: Database? = null
|
||||||
|
|
||||||
|
var jobTableName = "kjobJobs"
|
||||||
|
|
||||||
|
var lockTableName = "kjobLocks"
|
||||||
|
|
||||||
|
var expireLockInMinutes = 5L
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
{
|
{
|
||||||
"type": "agent-extracted",
|
"type": "agent-extracted",
|
||||||
"classes": [
|
"classes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -1,3 +1,2 @@
|
||||||
[
|
[
|
||||||
|
|
||||||
]
|
]
|
|
@ -691,55 +691,46 @@
|
||||||
{
|
{
|
||||||
"name": "getCreatedAt",
|
"name": "getCreatedAt",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getId",
|
"name": "getId",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getOverview",
|
"name": "getOverview",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getReplyId",
|
"name": "getReplyId",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getRepostId",
|
"name": "getRepostId",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getText",
|
"name": "getText",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getUrl",
|
"name": "getUrl",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getUserId",
|
"name": "getUserId",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getVisibility",
|
"name": "getVisibility",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -753,7 +744,6 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -765,19 +755,16 @@
|
||||||
{
|
{
|
||||||
"name": "getActor",
|
"name": "getActor",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getObject",
|
"name": "getObject",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setActor",
|
"name": "setActor",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -794,7 +781,6 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -805,7 +791,6 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -819,7 +804,6 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -831,7 +815,6 @@
|
||||||
{
|
{
|
||||||
"name": "getObject",
|
"name": "getObject",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -845,37 +828,31 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getActor",
|
"name": "getActor",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getObject",
|
"name": "getObject",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setActor",
|
"name": "setActor",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setObject",
|
"name": "setObject",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -889,13 +866,11 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -909,19 +884,16 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getContext",
|
"name": "getContext",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setContext",
|
"name": "setContext",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -935,49 +907,41 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getId",
|
"name": "getId",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getOwner",
|
"name": "getOwner",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getPublicKeyPem",
|
"name": "getPublicKeyPem",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setId",
|
"name": "setId",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setOwner",
|
"name": "setOwner",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setPublicKeyPem",
|
"name": "setPublicKeyPem",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -991,67 +955,56 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getAttributedTo",
|
"name": "getAttributedTo",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getContent",
|
"name": "getContent",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getId",
|
"name": "getId",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getPublished",
|
"name": "getPublished",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getTo",
|
"name": "getTo",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setAttributedTo",
|
"name": "setAttributedTo",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setContent",
|
"name": "setContent",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setId",
|
"name": "setId",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setPublished",
|
"name": "setPublished",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1065,25 +1018,21 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getName",
|
"name": "getName",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setName",
|
"name": "setName",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1094,7 +1043,6 @@
|
||||||
{
|
{
|
||||||
"name": "add",
|
"name": "add",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1108,7 +1056,6 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1121,49 +1068,41 @@
|
||||||
{
|
{
|
||||||
"name": "getInbox",
|
"name": "getInbox",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getOutbox",
|
"name": "getOutbox",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getPreferredUsername",
|
"name": "getPreferredUsername",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getPublicKey",
|
"name": "getPublicKey",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getSummary",
|
"name": "getSummary",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setInbox",
|
"name": "setInbox",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setOutbox",
|
"name": "setOutbox",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "setPreferredUsername",
|
"name": "setPreferredUsername",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1175,7 +1114,6 @@
|
||||||
{
|
{
|
||||||
"name": "setSummary",
|
"name": "setSummary",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1186,7 +1124,6 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1199,7 +1136,6 @@
|
||||||
{
|
{
|
||||||
"name": "<init>",
|
"name": "<init>",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1228,7 +1164,6 @@
|
||||||
{
|
{
|
||||||
"name": "parseActivity",
|
"name": "parseActivity",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1324,7 +1259,6 @@
|
||||||
{
|
{
|
||||||
"name": "init",
|
"name": "init",
|
||||||
"parameterTypes": [
|
"parameterTypes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,11 +44,10 @@
|
||||||
"pattern": "\\Qstatic/index.html\\E"
|
"pattern": "\\Qstatic/index.html\\E"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pattern":"\\Qkotlin/kotlin.kotlin_builtins\\E"
|
"pattern": "\\Qkotlin/kotlin.kotlin_builtins\\E"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"bundles": [
|
"bundles": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
{
|
{
|
||||||
"lambdaCapturingTypes": [
|
"lambdaCapturingTypes": [
|
||||||
|
|
||||||
],
|
],
|
||||||
"types": [
|
"types": [
|
||||||
|
|
||||||
],
|
],
|
||||||
"proxies": [
|
"proxies": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -2,6 +2,5 @@ package dev.usbharu.hideout
|
||||||
|
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
|
|
||||||
fun Application.empty(){
|
fun Application.empty() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,8 @@ class ContextDeserializerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
val readValue = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false).readValue<Follow>(s)
|
val readValue = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||||
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).readValue<Follow>(s)
|
||||||
println(readValue)
|
println(readValue)
|
||||||
println(readValue.actor)
|
println(readValue.actor)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ 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 org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
class ContextSerializerTest{
|
class ContextSerializerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun serialize() {
|
fun serialize() {
|
||||||
|
|
|
@ -15,7 +15,6 @@ import java.time.Instant
|
||||||
class ActivityPubKtTest {
|
class ActivityPubKtTest {
|
||||||
@Test
|
@Test
|
||||||
fun HttpSignTest(): Unit = runBlocking {
|
fun HttpSignTest(): Unit = runBlocking {
|
||||||
|
|
||||||
val ktorKeyMap = KtorKeyMap(object : IUserRepository {
|
val ktorKeyMap = KtorKeyMap(object : IUserRepository {
|
||||||
override suspend fun save(user: User): User {
|
override suspend fun save(user: User): User {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
|
@ -33,7 +32,7 @@ class ActivityPubKtTest {
|
||||||
TODO()
|
TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByNameAndDomain(name: String, domain: String): User? {
|
override suspend fun findByNameAndDomain(name: String, domain: String): User {
|
||||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||||
keyPairGenerator.initialize(1024)
|
keyPairGenerator.initialize(1024)
|
||||||
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
||||||
|
@ -96,12 +95,13 @@ class ActivityPubKtTest {
|
||||||
override suspend fun nextId(): Long {
|
override suspend fun nextId(): Long {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
val httpClient = HttpClient(MockEngine { httpRequestData ->
|
val httpClient = HttpClient(
|
||||||
|
MockEngine { httpRequestData ->
|
||||||
respondOk()
|
respondOk()
|
||||||
}) {
|
}
|
||||||
|
) {
|
||||||
install(httpSignaturePlugin) {
|
install(httpSignaturePlugin) {
|
||||||
keyMap = ktorKeyMap
|
keyMap = ktorKeyMap
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,5 @@ class ActivityPubKtTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
httpClient.postAp("https://localhost", "test", JsonLd(emptyList()))
|
httpClient.postAp("https://localhost", "test", JsonLd(emptyList()))
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ class KtorKeyMapTest {
|
||||||
TODO()
|
TODO()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByNameAndDomain(name: String, domain: String): User? {
|
override suspend fun findByNameAndDomain(name: String, domain: String): User {
|
||||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||||
keyPairGenerator.initialize(1024)
|
keyPairGenerator.initialize(1024)
|
||||||
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
||||||
|
@ -46,7 +46,6 @@ class KtorKeyMapTest {
|
||||||
generateKeyPair.private.toPem(),
|
generateKeyPair.private.toPem(),
|
||||||
createdAt = Instant.now()
|
createdAt = Instant.now()
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun findByDomain(domain: String): List<User> {
|
override suspend fun findByDomain(domain: String): List<User> {
|
||||||
|
@ -92,7 +91,6 @@ class KtorKeyMapTest {
|
||||||
override suspend fun nextId(): Long {
|
override suspend fun nextId(): Long {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ktorKeyMap.getPrivateKey("test")
|
ktorKeyMap.getPrivateKey("test")
|
||||||
|
|
|
@ -18,7 +18,6 @@ import java.time.Clock
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
|
|
||||||
|
|
||||||
class UserRepositoryTest {
|
class UserRepositoryTest {
|
||||||
|
|
||||||
lateinit var db: Database
|
lateinit var db: Database
|
||||||
|
@ -35,7 +34,6 @@ class UserRepositoryTest {
|
||||||
@AfterEach
|
@AfterEach
|
||||||
fun tearDown() {
|
fun tearDown() {
|
||||||
transaction(db) {
|
transaction(db) {
|
||||||
|
|
||||||
SchemaUtils.drop(UsersFollowers)
|
SchemaUtils.drop(UsersFollowers)
|
||||||
SchemaUtils.drop(Users)
|
SchemaUtils.drop(Users)
|
||||||
}
|
}
|
||||||
|
@ -43,11 +41,14 @@ class UserRepositoryTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `findFollowersById フォロワー一覧を取得`() = runTest {
|
fun `findFollowersById フォロワー一覧を取得`() = runTest {
|
||||||
val userRepository = UserRepository(db, object : IdGenerateService {
|
val userRepository = UserRepository(
|
||||||
|
db,
|
||||||
|
object : IdGenerateService {
|
||||||
override suspend fun generateId(): Long {
|
override suspend fun generateId(): Long {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
val user = userRepository.save(
|
val user = userRepository.save(
|
||||||
User(
|
User(
|
||||||
id = 0L,
|
id = 0L,
|
||||||
|
@ -98,18 +99,21 @@ class UserRepositoryTest {
|
||||||
userRepository.findFollowersById(user.id).let {
|
userRepository.findFollowersById(user.id).let {
|
||||||
assertIterableEquals(listOf(follower, follower2), it)
|
assertIterableEquals(listOf(follower, follower2), it)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `createFollower フォロワー追加`() = runTest {
|
fun `createFollower フォロワー追加`() = runTest {
|
||||||
val userRepository = UserRepository(db, object : IdGenerateService {
|
val userRepository = UserRepository(
|
||||||
|
db,
|
||||||
|
object : IdGenerateService {
|
||||||
override suspend fun generateId(): Long {
|
override suspend fun generateId(): Long {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
val user = userRepository.save(
|
val user = userRepository.save(
|
||||||
User(0L,
|
User(
|
||||||
|
0L,
|
||||||
"test",
|
"test",
|
||||||
"example.com",
|
"example.com",
|
||||||
"testUser",
|
"testUser",
|
||||||
|
@ -123,7 +127,8 @@ class UserRepositoryTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val follower = userRepository.save(
|
val follower = userRepository.save(
|
||||||
User(1L,
|
User(
|
||||||
|
1L,
|
||||||
"follower",
|
"follower",
|
||||||
"follower.example.com",
|
"follower.example.com",
|
||||||
"followerUser",
|
"followerUser",
|
||||||
|
@ -138,11 +143,9 @@ class UserRepositoryTest {
|
||||||
)
|
)
|
||||||
userRepository.createFollower(user.id, follower.id)
|
userRepository.createFollower(user.id, follower.id)
|
||||||
transaction {
|
transaction {
|
||||||
|
|
||||||
val followerIds =
|
val followerIds =
|
||||||
UsersFollowers.select { UsersFollowers.userId eq user.id }.map { it[UsersFollowers.followerId] }
|
UsersFollowers.select { UsersFollowers.userId eq user.id }.map { it[UsersFollowers.followerId] }
|
||||||
assertIterableEquals(listOf(follower.id), followerIds)
|
assertIterableEquals(listOf(follower.id), followerIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import dev.usbharu.hideout.plugins.configureStatusPages
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubService
|
||||||
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
|
||||||
import dev.usbharu.hideout.service.impl.IUserService
|
import dev.usbharu.hideout.service.impl.IUserService
|
||||||
import dev.usbharu.hideout.service.impl.UserService
|
|
||||||
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
import dev.usbharu.hideout.service.signature.HttpSignatureVerifyService
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
@ -28,7 +27,7 @@ class InboxRoutingKtTest {
|
||||||
}
|
}
|
||||||
application {
|
application {
|
||||||
configureSerialization()
|
configureSerialization()
|
||||||
configureRouting(mock(), mock(), mock(), mock(),mock())
|
configureRouting(mock(), mock(), mock(), mock(), mock())
|
||||||
}
|
}
|
||||||
client.get("/inbox").let {
|
client.get("/inbox").let {
|
||||||
Assertions.assertEquals(HttpStatusCode.MethodNotAllowed, it.status)
|
Assertions.assertEquals(HttpStatusCode.MethodNotAllowed, it.status)
|
||||||
|
@ -40,10 +39,10 @@ class InboxRoutingKtTest {
|
||||||
environment {
|
environment {
|
||||||
config = ApplicationConfig("empty.conf")
|
config = ApplicationConfig("empty.conf")
|
||||||
}
|
}
|
||||||
val httpSignatureVerifyService = mock<HttpSignatureVerifyService>{
|
val httpSignatureVerifyService = mock<HttpSignatureVerifyService> {
|
||||||
on { verify(any()) } doReturn true
|
on { verify(any()) } doReturn true
|
||||||
}
|
}
|
||||||
val activityPubService = mock<ActivityPubService>{
|
val activityPubService = mock<ActivityPubService> {
|
||||||
on { parseActivity(any()) } doThrow JsonParseException()
|
on { parseActivity(any()) } doThrow JsonParseException()
|
||||||
}
|
}
|
||||||
val userService = mock<IUserService>()
|
val userService = mock<IUserService>()
|
||||||
|
@ -51,7 +50,13 @@ class InboxRoutingKtTest {
|
||||||
application {
|
application {
|
||||||
configureStatusPages()
|
configureStatusPages()
|
||||||
configureSerialization()
|
configureSerialization()
|
||||||
configureRouting(httpSignatureVerifyService, activityPubService, userService, activityPubUserService,mock())
|
configureRouting(
|
||||||
|
httpSignatureVerifyService,
|
||||||
|
activityPubService,
|
||||||
|
userService,
|
||||||
|
activityPubUserService,
|
||||||
|
mock()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
client.post("/inbox").let {
|
client.post("/inbox").let {
|
||||||
Assertions.assertEquals(HttpStatusCode.BadRequest, it.status)
|
Assertions.assertEquals(HttpStatusCode.BadRequest, it.status)
|
||||||
|
@ -65,7 +70,7 @@ class InboxRoutingKtTest {
|
||||||
}
|
}
|
||||||
application {
|
application {
|
||||||
configureSerialization()
|
configureSerialization()
|
||||||
configureRouting(mock(), mock(), mock(), mock(),mock())
|
configureRouting(mock(), mock(), mock(), mock(), mock())
|
||||||
}
|
}
|
||||||
client.get("/users/test/inbox").let {
|
client.get("/users/test/inbox").let {
|
||||||
Assertions.assertEquals(HttpStatusCode.MethodNotAllowed, it.status)
|
Assertions.assertEquals(HttpStatusCode.MethodNotAllowed, it.status)
|
||||||
|
@ -77,10 +82,10 @@ class InboxRoutingKtTest {
|
||||||
environment {
|
environment {
|
||||||
config = ApplicationConfig("empty.conf")
|
config = ApplicationConfig("empty.conf")
|
||||||
}
|
}
|
||||||
val httpSignatureVerifyService = mock<HttpSignatureVerifyService>{
|
val httpSignatureVerifyService = mock<HttpSignatureVerifyService> {
|
||||||
on { verify(any()) } doReturn true
|
on { verify(any()) } doReturn true
|
||||||
}
|
}
|
||||||
val activityPubService = mock<ActivityPubService>{
|
val activityPubService = mock<ActivityPubService> {
|
||||||
on { parseActivity(any()) } doThrow JsonParseException()
|
on { parseActivity(any()) } doThrow JsonParseException()
|
||||||
}
|
}
|
||||||
val userService = mock<IUserService>()
|
val userService = mock<IUserService>()
|
||||||
|
@ -88,7 +93,13 @@ class InboxRoutingKtTest {
|
||||||
application {
|
application {
|
||||||
configureStatusPages()
|
configureStatusPages()
|
||||||
configureSerialization()
|
configureSerialization()
|
||||||
configureRouting(httpSignatureVerifyService, activityPubService, userService, activityPubUserService,mock())
|
configureRouting(
|
||||||
|
httpSignatureVerifyService,
|
||||||
|
activityPubService,
|
||||||
|
userService,
|
||||||
|
activityPubUserService,
|
||||||
|
mock()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
client.post("/users/test/inbox").let {
|
client.post("/users/test/inbox").let {
|
||||||
Assertions.assertEquals(HttpStatusCode.BadRequest, it.status)
|
Assertions.assertEquals(HttpStatusCode.BadRequest, it.status)
|
||||||
|
|
|
@ -32,7 +32,6 @@ import java.time.Instant
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
|
||||||
class UsersAPTest {
|
class UsersAPTest {
|
||||||
|
|
||||||
@Test()
|
@Test()
|
||||||
|
@ -100,8 +99,8 @@ class UsersAPTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Disabled
|
||||||
@Test()
|
@Test()
|
||||||
// @Disabled
|
|
||||||
fun `ユーザのURLにAcceptヘッダーをActivityとJson-LDにしてアクセスしたときPersonが返ってくる`() = testApplication {
|
fun `ユーザのURLにAcceptヘッダーをActivityとJson-LDにしてアクセスしたときPersonが返ってくる`() = testApplication {
|
||||||
environment {
|
environment {
|
||||||
config = ApplicationConfig("empty.conf")
|
config = ApplicationConfig("empty.conf")
|
||||||
|
@ -167,16 +166,21 @@ class UsersAPTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Disabled
|
||||||
@Test
|
@Test
|
||||||
// @Disabled
|
|
||||||
fun contentType_Test() {
|
fun contentType_Test() {
|
||||||
|
|
||||||
assertTrue(ContentType.Application.Activity.match("application/activity+json"))
|
assertTrue(ContentType.Application.Activity.match("application/activity+json"))
|
||||||
val listOf = listOf(ContentType.Application.JsonLd, ContentType.Application.Activity)
|
val listOf = listOf(ContentType.Application.JsonLd, ContentType.Application.Activity)
|
||||||
assertTrue(listOf.find { contentType ->
|
assertTrue(
|
||||||
|
listOf.find { contentType ->
|
||||||
contentType.match("application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
|
contentType.match("application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
|
||||||
}.let { it != null })
|
}.let { it != null }
|
||||||
assertTrue(ContentType.Application.JsonLd.match("application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""))
|
)
|
||||||
|
assertTrue(
|
||||||
|
ContentType.Application.JsonLd.match(
|
||||||
|
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package dev.usbharu.hideout.service
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
//import kotlinx.coroutines.NonCancellable.message
|
// import kotlinx.coroutines.NonCancellable.message
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -16,17 +16,13 @@ class TwitterSnowflakeIdGenerateServiceTest {
|
||||||
val mutex = Mutex()
|
val mutex = Mutex()
|
||||||
val mutableListOf = mutableListOf<Long>()
|
val mutableListOf = mutableListOf<Long>()
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
|
|
||||||
repeat(500000) {
|
repeat(500000) {
|
||||||
|
|
||||||
launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
val id = TwitterSnowflakeIdGenerateService.generateId()
|
val id = TwitterSnowflakeIdGenerateService.generateId()
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
mutableListOf.add(id)
|
mutableListOf.add(id)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,8 @@ class ActivityPubFollowServiceImplTest {
|
||||||
mock(),
|
mock(),
|
||||||
activityPubUserService,
|
activityPubUserService,
|
||||||
userService,
|
userService,
|
||||||
HttpClient(MockEngine { httpRequestData ->
|
HttpClient(
|
||||||
|
MockEngine { httpRequestData ->
|
||||||
assertEquals(person.inbox, httpRequestData.url.toString())
|
assertEquals(person.inbox, httpRequestData.url.toString())
|
||||||
val accept = Accept(
|
val accept = Accept(
|
||||||
type = emptyList(),
|
type = emptyList(),
|
||||||
|
@ -143,7 +144,8 @@ class ActivityPubFollowServiceImplTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
respondOk()
|
respondOk()
|
||||||
})
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
activityPubFollowService.receiveFollowJob(
|
activityPubFollowService.receiveFollowJob(
|
||||||
JobProps(
|
JobProps(
|
||||||
|
|
|
@ -73,7 +73,13 @@ class ActivityPubNoteServiceImplTest {
|
||||||
val jobQueueParentService = mock<JobQueueParentService>()
|
val jobQueueParentService = mock<JobQueueParentService>()
|
||||||
val activityPubNoteService = ActivityPubNoteServiceImpl(mock(), jobQueueParentService, userService)
|
val activityPubNoteService = ActivityPubNoteServiceImpl(mock(), jobQueueParentService, userService)
|
||||||
val postEntity = PostEntity(
|
val postEntity = PostEntity(
|
||||||
1L, 1L, null, "test text", 1L, 1, "https://example.com"
|
1L,
|
||||||
|
1L,
|
||||||
|
null,
|
||||||
|
"test text",
|
||||||
|
1L,
|
||||||
|
1,
|
||||||
|
"https://example.com"
|
||||||
)
|
)
|
||||||
activityPubNoteService.createNote(postEntity)
|
activityPubNoteService.createNote(postEntity)
|
||||||
verify(jobQueueParentService, times(2)).schedule(eq(DeliverPostJob), any())
|
verify(jobQueueParentService, times(2)).schedule(eq(DeliverPostJob), any())
|
||||||
|
@ -82,10 +88,12 @@ class ActivityPubNoteServiceImplTest {
|
||||||
@Test
|
@Test
|
||||||
fun `createPostJob 新しい投稿のJob`() = runTest {
|
fun `createPostJob 新しい投稿のJob`() = runTest {
|
||||||
Config.configData = ConfigData(objectMapper = JsonObjectMapper.objectMapper)
|
Config.configData = ConfigData(objectMapper = JsonObjectMapper.objectMapper)
|
||||||
val httpClient = HttpClient(MockEngine { httpRequestData ->
|
val httpClient = HttpClient(
|
||||||
|
MockEngine { httpRequestData ->
|
||||||
assertEquals("https://follower.example.com/inbox", httpRequestData.url.toString())
|
assertEquals("https://follower.example.com/inbox", httpRequestData.url.toString())
|
||||||
respondOk()
|
respondOk()
|
||||||
})
|
}
|
||||||
|
)
|
||||||
val activityPubNoteService = ActivityPubNoteServiceImpl(httpClient, mock(), mock())
|
val activityPubNoteService = ActivityPubNoteServiceImpl(httpClient, mock(), mock())
|
||||||
activityPubNoteService.createNoteJob(
|
activityPubNoteService.createNoteJob(
|
||||||
JobProps(
|
JobProps(
|
||||||
|
|
|
@ -17,7 +17,7 @@ import java.security.KeyPairGenerator
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
class UserServiceTest{
|
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")
|
||||||
|
@ -43,21 +43,19 @@ class UserServiceTest{
|
||||||
assertEquals("example.com", firstValue.domain)
|
assertEquals("example.com", firstValue.domain)
|
||||||
assertEquals("https://example.com/users/test/inbox", firstValue.inbox)
|
assertEquals("https://example.com/users/test/inbox", firstValue.inbox)
|
||||||
assertEquals("https://example.com/users/test/outbox", firstValue.outbox)
|
assertEquals("https://example.com/users/test/outbox", firstValue.outbox)
|
||||||
assertEquals(generateKeyPair.public.toPem(),firstValue.publicKey)
|
assertEquals(generateKeyPair.public.toPem(), firstValue.publicKey)
|
||||||
assertEquals(generateKeyPair.private.toPem(),firstValue.privateKey)
|
assertEquals(generateKeyPair.private.toPem(), firstValue.privateKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
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<IUserRepository>{
|
|
||||||
onBlocking { nextId() } doReturn 113345L
|
onBlocking { nextId() } doReturn 113345L
|
||||||
}
|
}
|
||||||
val userService = UserService(userRepository,mock())
|
val userService = UserService(userRepository, mock())
|
||||||
val user = RemoteUserCreateDto(
|
val user = RemoteUserCreateDto(
|
||||||
"test",
|
"test",
|
||||||
"example.com",
|
"example.com",
|
||||||
|
@ -81,7 +79,7 @@ class UserServiceTest{
|
||||||
assertEquals("example.com", firstValue.domain)
|
assertEquals("example.com", firstValue.domain)
|
||||||
assertEquals("https://example.com/inbox", firstValue.inbox)
|
assertEquals("https://example.com/inbox", firstValue.inbox)
|
||||||
assertEquals("https://example.com/outbox", firstValue.outbox)
|
assertEquals("https://example.com/outbox", firstValue.outbox)
|
||||||
assertEquals("-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----",firstValue.publicKey)
|
assertEquals("-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", firstValue.publicKey)
|
||||||
assertNull(firstValue.privateKey)
|
assertNull(firstValue.privateKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import org.jetbrains.exposed.sql.Transaction
|
||||||
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
||||||
import org.junit.jupiter.api.extension.*
|
import org.junit.jupiter.api.extension.*
|
||||||
|
|
||||||
class DBResetInterceptor : BeforeAllCallback,AfterAllCallback,BeforeEachCallback,AfterEachCallback {
|
class DBResetInterceptor : BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
|
||||||
private lateinit var transactionAll: Transaction
|
private lateinit var transactionAll: Transaction
|
||||||
private lateinit var transactionEach: Transaction
|
private lateinit var transactionEach: Transaction
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import org.jetbrains.exposed.sql.Database
|
||||||
import org.jetbrains.exposed.sql.DatabaseConfig
|
import org.jetbrains.exposed.sql.DatabaseConfig
|
||||||
import org.junit.jupiter.api.extension.ExtendWith
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
|
||||||
|
|
||||||
@ExtendWith(DBResetInterceptor::class)
|
@ExtendWith(DBResetInterceptor::class)
|
||||||
abstract class DatabaseTestBase {
|
abstract class DatabaseTestBase {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -12,7 +11,8 @@ abstract class DatabaseTestBase {
|
||||||
Database.connect(
|
Database.connect(
|
||||||
"jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
|
"jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
|
||||||
driver = "org.h2.Driver",
|
driver = "org.h2.Driver",
|
||||||
databaseConfig = DatabaseConfig { useNestedTransactions = true })
|
databaseConfig = DatabaseConfig { useNestedTransactions = true }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue