mirror of https://github.com/usbharu/Hideout.git
feat: Followを受け取ったらprintするように
This commit is contained in:
parent
013bf02484
commit
08d36724f5
|
@ -49,6 +49,9 @@ dependencies {
|
||||||
|
|
||||||
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
|
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
|
||||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
||||||
|
|
||||||
|
implementation("io.ktor:ktor-client-core:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-client-cio:$ktor_version")
|
||||||
}
|
}
|
||||||
|
|
||||||
jib {
|
jib {
|
||||||
|
|
|
@ -36,8 +36,9 @@ fun Application.module() {
|
||||||
}
|
}
|
||||||
single<ConfigData> {
|
single<ConfigData> {
|
||||||
ConfigData(
|
ConfigData(
|
||||||
environment.config.property("hideout.hostname").getString(),
|
url = environment.config.propertyOrNull("hideout.url")?.getString()
|
||||||
jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
?: environment.config.property("hideout.hostname").getString(),
|
||||||
|
objectMapper = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||||
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
|
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -45,7 +46,7 @@ fun Application.module() {
|
||||||
single<IUserAuthRepository> { UserAuthRepository(get()) }
|
single<IUserAuthRepository> { UserAuthRepository(get()) }
|
||||||
single<IUserAuthService> { UserAuthService(get(), get()) }
|
single<IUserAuthService> { UserAuthService(get(), get()) }
|
||||||
single<UserService> { UserService(get()) }
|
single<UserService> { UserService(get()) }
|
||||||
single<ActivityPubUserService> { ActivityPubUserService(get(),get()) }
|
single<ActivityPubUserService> { ActivityPubUserService(get(), get(),get()) }
|
||||||
}
|
}
|
||||||
configureKoin(module)
|
configureKoin(module)
|
||||||
val configData by inject<ConfigData>()
|
val configData by inject<ConfigData>()
|
||||||
|
|
|
@ -2,7 +2,7 @@ package dev.usbharu.hideout.ap
|
||||||
|
|
||||||
open class Object : JsonLd {
|
open class Object : JsonLd {
|
||||||
private var type: List<String> = emptyList()
|
private var type: List<String> = emptyList()
|
||||||
private var name: String? = null
|
var name: String? = null
|
||||||
|
|
||||||
protected constructor()
|
protected constructor()
|
||||||
constructor(type: List<String>, name: String) : super() {
|
constructor(type: List<String>, name: String) : super() {
|
||||||
|
|
|
@ -2,8 +2,8 @@ package dev.usbharu.hideout.ap
|
||||||
|
|
||||||
open class Person : Object {
|
open class Person : Object {
|
||||||
private var id:String? = null
|
private var id:String? = null
|
||||||
private var preferredUsername:String? = null
|
var preferredUsername:String? = null
|
||||||
private var summary:String? = null
|
var summary:String? = null
|
||||||
private var inbox:String? = null
|
private var inbox:String? = null
|
||||||
private var outbox:String? = null
|
private var outbox:String? = null
|
||||||
private var url:String? = null
|
private var url:String? = null
|
||||||
|
|
|
@ -7,4 +7,8 @@ object Config {
|
||||||
var configData: ConfigData = ConfigData()
|
var configData: ConfigData = ConfigData()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ConfigData(val hostname: String = "", val objectMapper: ObjectMapper = jacksonObjectMapper())
|
data class ConfigData(
|
||||||
|
val url: String = "",
|
||||||
|
val domain: String = url.substringAfter("://").substringBeforeLast(":"),
|
||||||
|
val objectMapper: ObjectMapper = jacksonObjectMapper()
|
||||||
|
)
|
||||||
|
|
|
@ -2,19 +2,24 @@ package dev.usbharu.hideout.domain.model
|
||||||
|
|
||||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
import org.jetbrains.exposed.dao.id.LongIdTable
|
||||||
|
|
||||||
data class User(val name: String, val screenName: String, val description: String)
|
data class User(val name: String,val domain: String, val screenName: String, val description: String)
|
||||||
|
|
||||||
data class UserEntity(
|
data class UserEntity(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val name: String,
|
val name: String,
|
||||||
|
val domain:String,
|
||||||
val screenName: String,
|
val screenName: String,
|
||||||
val description: String
|
val description: String
|
||||||
) {
|
) {
|
||||||
constructor(id: Long, user: User) : this(id, user.name, user.screenName, user.description)
|
constructor(id: Long, user: User) : this(id, user.name,user.domain, user.screenName, user.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Users : LongIdTable("users") {
|
object Users : LongIdTable("users") {
|
||||||
val name = varchar("name", length = 64).uniqueIndex()
|
val name = varchar("name", length = 64)
|
||||||
|
val domain = varchar("domain", length = 255)
|
||||||
val screenName = varchar("screen_name", length = 64)
|
val screenName = varchar("screen_name", length = 64)
|
||||||
val description = varchar("description", length = 600)
|
val description = varchar("description", length = 600)
|
||||||
|
init {
|
||||||
|
uniqueIndex(name, domain)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,17 @@ import org.jetbrains.exposed.sql.ReferenceOption
|
||||||
|
|
||||||
data class UserAuthentication(
|
data class UserAuthentication(
|
||||||
val userId: Long,
|
val userId: Long,
|
||||||
val hash: String,
|
val hash: String?,
|
||||||
val publicKey: String,
|
val publicKey: String,
|
||||||
val privateKey: String
|
val privateKey: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UserAuthenticationEntity(
|
data class UserAuthenticationEntity(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val userId: Long,
|
val userId: Long,
|
||||||
val hash: String,
|
val hash: String?,
|
||||||
val publicKey: String,
|
val publicKey: String,
|
||||||
val privateKey: String
|
val privateKey: String?
|
||||||
) {
|
) {
|
||||||
constructor(id: Long, userAuthentication: UserAuthentication) : this(
|
constructor(id: Long, userAuthentication: UserAuthentication) : this(
|
||||||
id,
|
id,
|
||||||
|
@ -28,7 +28,7 @@ data class UserAuthenticationEntity(
|
||||||
|
|
||||||
object UsersAuthentication : LongIdTable("users_auth") {
|
object UsersAuthentication : LongIdTable("users_auth") {
|
||||||
val userId = long("user_id").references(Users.id, onUpdate = ReferenceOption.CASCADE)
|
val userId = long("user_id").references(Users.id, onUpdate = ReferenceOption.CASCADE)
|
||||||
val hash = varchar("hash", length = 64)
|
val hash = varchar("hash", length = 64).nullable()
|
||||||
val publicKey = varchar("public_key", length = 1000_000)
|
val publicKey = varchar("public_key", length = 1000_000)
|
||||||
val privateKey = varchar("private_key", length = 1000_000)
|
val privateKey = varchar("private_key", length = 1000_000).nullable()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package dev.usbharu.hideout.domain.model
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.dao.id.LongIdTable
|
||||||
|
|
||||||
|
object UsersFollowers : LongIdTable("users_followers") {
|
||||||
|
val userId = long("user_id").references(Users.id).index()
|
||||||
|
val followerId = long("follower_id").references(Users.id)
|
||||||
|
init {
|
||||||
|
uniqueIndex(userId, followerId)
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,4 +17,8 @@ interface IUserRepository {
|
||||||
suspend fun findAll(): List<User>
|
suspend fun findAll(): List<User>
|
||||||
|
|
||||||
suspend fun findAllByLimitAndByOffset(limit: Int, offset: Long = 0): List<UserEntity>
|
suspend fun findAllByLimitAndByOffset(limit: Int, offset: Long = 0): List<UserEntity>
|
||||||
|
|
||||||
|
suspend fun createFollower(id: Long, follower: Long)
|
||||||
|
suspend fun deleteFollower(id: Long, follower: Long)
|
||||||
|
suspend fun findFollowersById(id: Long): List<UserEntity>
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ class UserAuthRepository(private val database: Database) : IUserAuthRepository {
|
||||||
init {
|
init {
|
||||||
transaction(database) {
|
transaction(database) {
|
||||||
SchemaUtils.create(UsersAuthentication)
|
SchemaUtils.create(UsersAuthentication)
|
||||||
|
SchemaUtils.createMissingTablesAndColumns(UsersAuthentication)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package dev.usbharu.hideout.repository
|
||||||
import dev.usbharu.hideout.domain.model.User
|
import dev.usbharu.hideout.domain.model.User
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
import dev.usbharu.hideout.domain.model.UserEntity
|
||||||
import dev.usbharu.hideout.domain.model.Users
|
import dev.usbharu.hideout.domain.model.Users
|
||||||
|
import dev.usbharu.hideout.domain.model.UsersFollowers
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
@ -13,12 +14,16 @@ class UserRepository(private val database: Database) : IUserRepository {
|
||||||
init {
|
init {
|
||||||
transaction(database) {
|
transaction(database) {
|
||||||
SchemaUtils.create(Users)
|
SchemaUtils.create(Users)
|
||||||
|
SchemaUtils.create(UsersFollowers)
|
||||||
|
SchemaUtils.createMissingTablesAndColumns(Users)
|
||||||
|
SchemaUtils.createMissingTablesAndColumns(UsersFollowers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ResultRow.toUser(): User {
|
private fun ResultRow.toUser(): User {
|
||||||
return User(
|
return User(
|
||||||
this[Users.name],
|
this[Users.name],
|
||||||
|
this[Users.domain],
|
||||||
this[Users.screenName],
|
this[Users.screenName],
|
||||||
this[Users.description]
|
this[Users.description]
|
||||||
)
|
)
|
||||||
|
@ -28,6 +33,7 @@ class UserRepository(private val database: Database) : IUserRepository {
|
||||||
return UserEntity(
|
return UserEntity(
|
||||||
this[Users.id].value,
|
this[Users.id].value,
|
||||||
this[Users.name],
|
this[Users.name],
|
||||||
|
this[Users.domain],
|
||||||
this[Users.screenName],
|
this[Users.screenName],
|
||||||
this[Users.description]
|
this[Users.description]
|
||||||
)
|
)
|
||||||
|
@ -40,12 +46,22 @@ class UserRepository(private val database: Database) : IUserRepository {
|
||||||
return query {
|
return query {
|
||||||
UserEntity(Users.insert {
|
UserEntity(Users.insert {
|
||||||
it[name] = user.name
|
it[name] = user.name
|
||||||
|
it[domain] = user.domain
|
||||||
it[screenName] = user.screenName
|
it[screenName] = user.screenName
|
||||||
it[description] = user.description
|
it[description] = user.description
|
||||||
}[Users.id].value, user)
|
}[Users.id].value, user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun createFollower(id: Long, follower: Long) {
|
||||||
|
return query {
|
||||||
|
UsersFollowers.insert {
|
||||||
|
it[userId] = id
|
||||||
|
it[followerId] = follower
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun findById(id: Long): UserEntity? {
|
override suspend fun findById(id: Long): UserEntity? {
|
||||||
return query {
|
return query {
|
||||||
Users.select { Users.id eq id }.map {
|
Users.select { Users.id eq id }.map {
|
||||||
|
@ -62,11 +78,36 @@ class UserRepository(private val database: Database) : IUserRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun findFollowersById(id: Long): List<UserEntity> {
|
||||||
|
return query {
|
||||||
|
val followers = Users.alias("followers")
|
||||||
|
Users.leftJoin(
|
||||||
|
otherTable = UsersFollowers,
|
||||||
|
onColumn = { Users.id },
|
||||||
|
otherColumn = { UsersFollowers.userId })
|
||||||
|
.leftJoin(
|
||||||
|
otherTable = followers,
|
||||||
|
onColumn = { UsersFollowers.followerId },
|
||||||
|
otherColumn = { followers[Users.id] })
|
||||||
|
.select { Users.id eq id }
|
||||||
|
.map {
|
||||||
|
UserEntity(
|
||||||
|
id = it[followers[Users.id]].value,
|
||||||
|
name = it[followers[Users.name]],
|
||||||
|
domain = it[followers[Users.domain]],
|
||||||
|
screenName = it[followers[Users.screenName]],
|
||||||
|
description = it[followers[Users.description]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override suspend fun update(userEntity: UserEntity) {
|
override suspend fun update(userEntity: UserEntity) {
|
||||||
return query {
|
return query {
|
||||||
Users.update({ Users.id eq userEntity.id }) {
|
Users.update({ Users.id eq userEntity.id }) {
|
||||||
it[name] = userEntity.name
|
it[name] = userEntity.name
|
||||||
|
it[domain] = userEntity.domain
|
||||||
it[screenName] = userEntity.screenName
|
it[screenName] = userEntity.screenName
|
||||||
it[description] = userEntity.description
|
it[description] = userEntity.description
|
||||||
}
|
}
|
||||||
|
@ -79,6 +120,12 @@ class UserRepository(private val database: Database) : IUserRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteFollower(id: Long, follower: Long) {
|
||||||
|
query {
|
||||||
|
UsersFollowers.deleteWhere { (userId eq id).and(followerId eq follower) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun findAll(): List<User> {
|
override suspend fun findAll(): List<User> {
|
||||||
return query {
|
return query {
|
||||||
Users.selectAll().map { it.toUser() }
|
Users.selectAll().map { it.toUser() }
|
||||||
|
|
|
@ -50,7 +50,8 @@ fun Application.user(userService: UserService, activityPubUserService: ActivityP
|
||||||
val userModel = activityPubUserService.generateUserModel(name!!)
|
val userModel = activityPubUserService.generateUserModel(name!!)
|
||||||
return@get call.respondAp(userModel)
|
return@get call.respondAp(userModel)
|
||||||
}
|
}
|
||||||
|
name?.let { it1 -> userService.findByName(it1).id }
|
||||||
|
?.let { it2 -> println(userService.findFollowersById(it2)) }
|
||||||
val principal = call.principal<UserIdPrincipal>()
|
val principal = call.principal<UserIdPrincipal>()
|
||||||
if (principal != null && name != null) {
|
if (principal != null && name != null) {
|
||||||
// iUserService.findByName(name)
|
// iUserService.findByName(name)
|
||||||
|
|
|
@ -16,7 +16,7 @@ fun Application.wellKnown(userService: UserService) {
|
||||||
get("/host-meta") {
|
get("/host-meta") {
|
||||||
//language=XML
|
//language=XML
|
||||||
val xml = """
|
val xml = """
|
||||||
<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" type="application/xrd+xml" template="${Config.configData.hostname}/.well-known/webfinger?resource={uri}"/></XRD>
|
<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" type="application/xrd+xml" template="${Config.configData.url}/.well-known/webfinger?resource={uri}"/></XRD>
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
return@get call.respondText(
|
return@get call.respondText(
|
||||||
contentType = ContentType("application", "xrd+xml"),
|
contentType = ContentType("application", "xrd+xml"),
|
||||||
|
@ -32,7 +32,7 @@ fun Application.wellKnown(userService: UserService) {
|
||||||
{
|
{
|
||||||
"rel": "lrdd",
|
"rel": "lrdd",
|
||||||
"type": "application/jrd+json",
|
"type": "application/jrd+json",
|
||||||
"template": "${Config.configData.hostname}/.well-known/webfinger?resource={uri}"
|
"template": "${Config.configData.url}/.well-known/webfinger?resource={uri}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ fun Application.wellKnown(userService: UserService) {
|
||||||
WebFingerResource.Link(
|
WebFingerResource.Link(
|
||||||
rel = "self",
|
rel = "self",
|
||||||
type = ContentType.Application.Activity.toString(),
|
type = ContentType.Application.Activity.toString(),
|
||||||
href = "${Config.configData.hostname}/users/${userEntity.name}"
|
href = "${Config.configData.url}/users/${userEntity.name}"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,30 +1,38 @@
|
||||||
package dev.usbharu.hideout.routing
|
package dev.usbharu.hideout.routing
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.service.ActivityPubService
|
||||||
|
import dev.usbharu.hideout.util.HttpUtil
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.request.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
fun Application.userActivityPubRouting() {
|
fun Application.userActivityPubRouting(activityPubService: ActivityPubService) {
|
||||||
routing {
|
routing {
|
||||||
route("/users/{name}") {
|
route("/users/{name}") {
|
||||||
route("/inbox") {
|
route("/inbox") {
|
||||||
get {
|
get {
|
||||||
call.respond(HttpStatusCode.OK)
|
call.respond(HttpStatusCode.MethodNotAllowed)
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
call.respond(HttpStatusCode.OK)
|
if (!HttpUtil.isContentTypeOfActivityPub(call.request.contentType())) {
|
||||||
|
return@post call.respond(HttpStatusCode.BadRequest)
|
||||||
|
}
|
||||||
|
val bodyText = call.receiveText()
|
||||||
|
println(bodyText)
|
||||||
|
activityPubService.switchApType(bodyText)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
route("/outbox") {
|
route("/outbox") {
|
||||||
get {
|
get {
|
||||||
call.respond(HttpStatusCode.OK)
|
call.respond(HttpStatusCode.MethodNotAllowed)
|
||||||
|
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
|
|
||||||
call.respond(HttpStatusCode.OK)
|
call.respond(HttpStatusCode.MethodNotAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,20 @@
|
||||||
package dev.usbharu.hideout.service
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
class ActivityPubService {
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
|
||||||
|
class ActivityPubService(private val objectMapper: ObjectMapper) {
|
||||||
|
|
||||||
|
enum class ActivityType{
|
||||||
|
Follow,
|
||||||
|
Undo
|
||||||
|
}
|
||||||
|
|
||||||
|
fun switchApType(json:String):ActivityType{
|
||||||
|
val typeAsText = objectMapper.readTree(json).get("type").asText()
|
||||||
|
return when(typeAsText){
|
||||||
|
"Follow" -> ActivityType.Follow
|
||||||
|
"Undo" -> ActivityType.Undo
|
||||||
|
else -> throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,21 @@ import dev.usbharu.hideout.ap.Image
|
||||||
import dev.usbharu.hideout.ap.Key
|
import dev.usbharu.hideout.ap.Key
|
||||||
import dev.usbharu.hideout.ap.Person
|
import dev.usbharu.hideout.ap.Person
|
||||||
import dev.usbharu.hideout.config.Config
|
import dev.usbharu.hideout.config.Config
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.call.*
|
||||||
|
import io.ktor.client.plugins.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.http.*
|
||||||
|
|
||||||
class ActivityPubUserService(
|
class ActivityPubUserService(
|
||||||
|
private val httpClient:HttpClient,
|
||||||
private val userService: UserService,
|
private val userService: UserService,
|
||||||
private val userAuthService: IUserAuthService
|
private val userAuthService: IUserAuthService
|
||||||
) {
|
) {
|
||||||
suspend fun generateUserModel(name: String): Person {
|
suspend fun generateUserModel(name: String): Person {
|
||||||
val userEntity = userService.findByName(name)
|
val userEntity = userService.findByName(name)
|
||||||
val userAuthEntity = userAuthService.findByUserId(userEntity.id)
|
val userAuthEntity = userAuthService.findByUserId(userEntity.id)
|
||||||
val userUrl = "${Config.configData.hostname}/users/$name"
|
val userUrl = "${Config.configData.url}/users/$name"
|
||||||
return Person(
|
return Person(
|
||||||
type = emptyList(),
|
type = emptyList(),
|
||||||
name = userEntity.name,
|
name = userEntity.name,
|
||||||
|
@ -37,4 +43,19 @@ class ActivityPubUserService(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun fetchUserModel(url:String):Person? {
|
||||||
|
return try {
|
||||||
|
httpClient.get(url).body<Person>()
|
||||||
|
} catch (e: ResponseException) {
|
||||||
|
if (e.response.status == HttpStatusCode.NotFound) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun receiveFollow(){
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
|
class HttpSignService {
|
||||||
|
suspend fun sign(){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.domain.model.UserEntity
|
||||||
|
import dev.usbharu.hideout.webfinger.WebFinger
|
||||||
|
|
||||||
|
interface IWebFingerService {
|
||||||
|
suspend fun fetch(acct:String): WebFinger?
|
||||||
|
|
||||||
|
suspend fun sync(webFinger: WebFinger):UserEntity
|
||||||
|
|
||||||
|
suspend fun fetchAndSync(acct: String):UserEntity{
|
||||||
|
val webFinger = fetch(acct)?: throw IllegalArgumentException()
|
||||||
|
return sync(webFinger)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
package dev.usbharu.hideout.service
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.config.Config
|
||||||
import dev.usbharu.hideout.domain.model.User
|
import dev.usbharu.hideout.domain.model.User
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthentication
|
import dev.usbharu.hideout.domain.model.UserAuthentication
|
||||||
import dev.usbharu.hideout.domain.model.UserAuthenticationEntity
|
import dev.usbharu.hideout.domain.model.UserAuthenticationEntity
|
||||||
|
import dev.usbharu.hideout.domain.model.Users.screenName
|
||||||
import dev.usbharu.hideout.exception.UserNotFoundException
|
import dev.usbharu.hideout.exception.UserNotFoundException
|
||||||
import dev.usbharu.hideout.repository.IUserAuthRepository
|
import dev.usbharu.hideout.repository.IUserAuthRepository
|
||||||
import dev.usbharu.hideout.repository.IUserRepository
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
|
@ -10,7 +12,6 @@ import io.ktor.util.*
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.interfaces.RSAPrivateCrtKey
|
|
||||||
import java.security.interfaces.RSAPrivateKey
|
import java.security.interfaces.RSAPrivateKey
|
||||||
import java.security.interfaces.RSAPublicKey
|
import java.security.interfaces.RSAPublicKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -34,6 +35,7 @@ class UserAuthService(
|
||||||
override suspend fun registerAccount(username: String, hash: String) {
|
override suspend fun registerAccount(username: String, hash: String) {
|
||||||
val registerUser = User(
|
val registerUser = User(
|
||||||
name = username,
|
name = username,
|
||||||
|
domain = Config.configData.domain,
|
||||||
screenName = username,
|
screenName = username,
|
||||||
description = ""
|
description = ""
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,8 +4,6 @@ import dev.usbharu.hideout.domain.model.User
|
||||||
import dev.usbharu.hideout.domain.model.UserEntity
|
import dev.usbharu.hideout.domain.model.UserEntity
|
||||||
import dev.usbharu.hideout.exception.UserNotFoundException
|
import dev.usbharu.hideout.exception.UserNotFoundException
|
||||||
import dev.usbharu.hideout.repository.IUserRepository
|
import dev.usbharu.hideout.repository.IUserRepository
|
||||||
import dev.usbharu.hideout.repository.UserRepository
|
|
||||||
import org.jetbrains.exposed.sql.Database
|
|
||||||
import java.lang.Integer.min
|
import java.lang.Integer.min
|
||||||
|
|
||||||
class UserService(private val userRepository: IUserRepository) {
|
class UserService(private val userRepository: IUserRepository) {
|
||||||
|
@ -24,10 +22,20 @@ class UserService(private val userRepository: IUserRepository) {
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findByName(name: String): UserEntity {
|
suspend fun findByName(name: String): UserEntity {
|
||||||
return userRepository.findByName(name) ?: throw UserNotFoundException("$name was not found.")
|
return userRepository.findByName(name)
|
||||||
|
?: throw UserNotFoundException("$name was not found.")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun create(user: User): UserEntity {
|
suspend fun create(user: User): UserEntity {
|
||||||
return userRepository.create(user)
|
return userRepository.create(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun findFollowersById(id: Long): List<UserEntity> {
|
||||||
|
return userRepository.findFollowersById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun addFollowers(id: Long, follower: Long) {
|
||||||
|
return userRepository.createFollower(id, follower)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package dev.usbharu.hideout.service
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.domain.model.User
|
||||||
|
import dev.usbharu.hideout.domain.model.UserEntity
|
||||||
|
import dev.usbharu.hideout.util.HttpUtil
|
||||||
|
import dev.usbharu.hideout.webfinger.WebFinger
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.call.*
|
||||||
|
import io.ktor.client.plugins.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.http.*
|
||||||
|
|
||||||
|
class WebFingerService(
|
||||||
|
private val httpClient: HttpClient,
|
||||||
|
private val userService: UserService,
|
||||||
|
private val userAuthService: IUserAuthService,
|
||||||
|
private val activityPubUserService: ActivityPubUserService
|
||||||
|
) : IWebFingerService {
|
||||||
|
override suspend fun fetch(acct: String): WebFinger? {
|
||||||
|
|
||||||
|
val fullName = acct.substringAfter("acct:")
|
||||||
|
val domain = fullName.substringAfterLast("@")
|
||||||
|
|
||||||
|
return try {
|
||||||
|
httpClient.get("https://$domain/.well-known/webfinger?resource=acct:$fullName")
|
||||||
|
.body<WebFinger>()
|
||||||
|
} catch (e: ResponseException) {
|
||||||
|
if (e.response.status == HttpStatusCode.NotFound) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun sync(webFinger: WebFinger): UserEntity {
|
||||||
|
|
||||||
|
val link = webFinger.links.find {
|
||||||
|
it.rel == "self" && HttpUtil.isContentTypeOfActivityPub(
|
||||||
|
ContentType.parse(
|
||||||
|
it.type.orEmpty()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}?.href ?: throw Exception()
|
||||||
|
|
||||||
|
val fullName = webFinger.subject.substringAfter("acct:")
|
||||||
|
val domain = fullName.substringAfterLast("@")
|
||||||
|
val userName = fullName.substringBeforeLast("@")
|
||||||
|
|
||||||
|
val userModel = activityPubUserService.fetchUserModel(link) ?: throw Exception()
|
||||||
|
|
||||||
|
val user = User(
|
||||||
|
userModel.preferredUsername ?: throw IllegalStateException(),
|
||||||
|
domain,
|
||||||
|
userName,
|
||||||
|
userModel.summary.orEmpty()
|
||||||
|
)
|
||||||
|
return userService.create(user)
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,11 +15,15 @@ object HttpUtil {
|
||||||
if (subType == "activity+json") {
|
if (subType == "activity+json") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (subType == "ld+json") {
|
return subType == "ld+json"
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
|
fun isContentTypeOfActivityPub(contentType: ContentType): Boolean {
|
||||||
|
return isContentTypeOfActivityPub(
|
||||||
|
contentType.contentType,
|
||||||
|
contentType.contentSubtype,
|
||||||
|
contentType.parameter("profile").orEmpty()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val ContentType.Application.Activity: ContentType
|
val ContentType.Application.Activity: ContentType
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package dev.usbharu.hideout.webfinger
|
||||||
|
|
||||||
|
class WebFinger(val subject: String, val aliases: List<String>, val links: List<Link>) {
|
||||||
|
class Link(val rel: String, val type: String?, val href: String?, val template: String)
|
||||||
|
}
|
Loading…
Reference in New Issue