1
0
Fork 1
mirror of https://gitlab.com/mangadex-pub/mangadex_at_home.git synced 2024-01-19 02:48:37 +00:00

Add external_ip and refactor pings

This commit is contained in:
carbotaniuman 2021-01-28 07:50:13 -06:00
parent 49e9523a7c
commit 5be2c21028
7 changed files with 108 additions and 64 deletions

View file

@ -66,6 +66,11 @@ server_settings:
# The external hostname to listen on # The external hostname to listen on
# Keep this at 0.0.0.0 unless you know what you're doing # Keep this at 0.0.0.0 unless you know what you're doing
hostname: 0.0.0.0 hostname: 0.0.0.0
# The external ip to broadcast to the webserver
# The default of null means the backend will infer it
# from where it was sent from, which may fail in the
# presence of multiple IPs
external_ip: ~
# Maximum mebibytes per hour of images to server # Maximum mebibytes per hour of images to server
# Setting this to 0 disables the limiter # Setting this to 0 disables the limiter
max_mebibytes_per_hour: 0 max_mebibytes_per_hour: 0

View file

@ -22,22 +22,17 @@ import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import mdnet.ServerHandlerJackson.auto import mdnet.ServerHandlerJackson.auto
import mdnet.logging.info import mdnet.logging.info
import mdnet.settings.ClientSettings import mdnet.settings.*
import mdnet.settings.RemoteSettings
import org.apache.hc.client5.http.impl.DefaultSchemePortResolver
import org.apache.hc.client5.http.impl.classic.HttpClients
import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner
import org.apache.hc.core5.http.HttpHost
import org.apache.hc.core5.http.protocol.HttpContext
import org.http4k.client.ApacheClient import org.http4k.client.ApacheClient
import org.http4k.core.Body import org.http4k.core.Body
import org.http4k.core.HttpHandler
import org.http4k.core.Method import org.http4k.core.Method
import org.http4k.core.Request import org.http4k.core.Request
import org.http4k.format.ConfigurableJackson import org.http4k.format.ConfigurableJackson
import org.http4k.format.asConfigurable import org.http4k.format.asConfigurable
import org.http4k.format.withStandardMappings import org.http4k.format.withStandardMappings
import org.http4k.lens.LensFailure
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.net.InetAddress
object ServerHandlerJackson : ConfigurableJackson( object ServerHandlerJackson : ConfigurableJackson(
KotlinModule() KotlinModule()
@ -49,94 +44,88 @@ object ServerHandlerJackson : ConfigurableJackson(
class BackendApi(private val settings: ClientSettings) { class BackendApi(private val settings: ClientSettings) {
private val serverAddress = settings.devSettings.devUrl ?: SERVER_ADDRESS private val serverAddress = settings.devSettings.devUrl ?: SERVER_ADDRESS
private val client = ApacheClient( private val client = ApacheClient()
client = HttpClients.custom()
.setRoutePlanner(
object : DefaultRoutePlanner(DefaultSchemePortResolver()) {
override fun determineLocalAddress(firstHop: HttpHost?, context: HttpContext?): InetAddress {
return InetAddress.getByName(settings.serverSettings.hostname)
}
}
)
.build()
)
fun logoutFromControl(): Boolean { fun logoutFromControl(): Boolean {
val serverSettings = settings.serverSettings
LOGGER.info { "Disconnecting from the control server" } LOGGER.info { "Disconnecting from the control server" }
val params = mapOf<String, Any>(
"secret" to settings.serverSettings.secret val request = LOGOUT_REQUEST_LENS(
LogoutRequest(serverSettings.secret),
Request(Method.POST, serverAddress + "stop")
) )
val request = STRING_ANY_MAP_LENS(params, Request(Method.POST, serverAddress + "stop"))
val response = client(request) val response = client(request)
return response.status.successful return response.status.successful
} }
private fun getPingParams(tlsCreatedAt: String? = null): Map<String, Any> { private fun getPingParams(tlsCreatedAt: String? = null): SettingsRequest {
val serverSettings = settings.serverSettings val serverSettings = settings.serverSettings
return mapOf( return SettingsRequest(
"secret" to serverSettings.secret, secret = serverSettings.secret,
"port" to let { port = if (serverSettings.externalPort != 0) {
if (serverSettings.externalPort != 0) { serverSettings.externalPort
serverSettings.externalPort
} else {
serverSettings.port
}
},
"disk_space" to settings.maxCacheSizeInMebibytes * 1024 * 1024,
"network_speed" to serverSettings.externalMaxKilobitsPerSecond * 1000 / 8,
"build_version" to Constants.CLIENT_BUILD
).let {
if (tlsCreatedAt != null) {
it.plus("tls_created_at" to tlsCreatedAt)
} else { } else {
it serverSettings.port
} },
} buildVersion = Constants.CLIENT_BUILD,
diskSpace = settings.maxCacheSizeInMebibytes * 1024 * 1024,
networkSpeed = serverSettings.externalMaxKilobitsPerSecond * 1000 / 8,
ipAddress = serverSettings.externalIp,
tlsCreatedAt = tlsCreatedAt,
)
} }
fun loginToControl(): RemoteSettings? { fun loginToControl(): PingResult {
LOGGER.info { "Connecting to the control server" } LOGGER.info { "Connecting to the control server" }
val request = STRING_ANY_MAP_LENS( val request = SETTINGS_REQUEST_LENS(
getPingParams(null), getPingParams(null),
Request( Request(
Method.POST, Method.POST,
serverAddress + "ping" serverAddress + "ping"
) )
) )
val response = client(request) return client.makeRequest(request)
return if (response.status.successful) {
SERVER_SETTINGS_LENS(response)
} else {
null
}
} }
fun pingControl(old: RemoteSettings): RemoteSettings? { fun pingControl(old: RemoteSettings): PingResult {
LOGGER.info { "Pinging the control server" } LOGGER.info { "Pinging the control server" }
val request = STRING_ANY_MAP_LENS( val request = SETTINGS_REQUEST_LENS(
getPingParams(old.tls!!.createdAt), getPingParams(old.tls!!.createdAt),
Request( Request(
Method.POST, Method.POST,
serverAddress + "ping" serverAddress + "ping"
) )
) )
val response = client(request) return client.makeRequest(request)
}
return if (response.status.successful) { private fun HttpHandler.makeRequest(request: Request): PingResult {
SERVER_SETTINGS_LENS(response) val response = this(request)
} else {
null return when {
response.status.successful -> {
SERVER_SETTINGS_LENS(response)
}
else -> {
try {
PING_FAILURE_LENS(response)
} catch (e: LensFailure) {
PingFailure(response.status.code, response.status.description)
}
}
} }
} }
companion object { companion object {
private val LOGGER = LoggerFactory.getLogger(BackendApi::class.java) private val LOGGER = LoggerFactory.getLogger(BackendApi::class.java)
private val STRING_ANY_MAP_LENS = Body.auto<Map<String, Any>>().toLens() private val SETTINGS_REQUEST_LENS = Body.auto<SettingsRequest>().toLens()
private val PING_FAILURE_LENS = Body.auto<PingFailure>().toLens()
private val LOGOUT_REQUEST_LENS = Body.auto<LogoutRequest>().toLens()
private val SERVER_SETTINGS_LENS = Body.auto<RemoteSettings>().toLens() private val SERVER_SETTINGS_LENS = Body.auto<RemoteSettings>().toLens()
private const val SERVER_ADDRESS = "https://api.mangadex.network/" private const val SERVER_ADDRESS = "https://api.mangadex.network/"
} }

View file

@ -23,7 +23,6 @@ import mdnet.logging.error
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import picocli.CommandLine import picocli.CommandLine
import java.io.File import java.io.File
import java.lang.Exception
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -88,6 +87,7 @@ class ClientArgs(
val client = MangaDexClient(settingsFile, databaseFile, cacheFolder) val client = MangaDexClient(settingsFile, databaseFile, cacheFolder)
val hook = Thread { val hook = Thread {
println("A")
client.shutdown() client.shutdown()
(LoggerFactory.getILoggerFactory() as LoggerContext).stop() (LoggerFactory.getILoggerFactory() as LoggerContext).stop()
} }
@ -97,7 +97,7 @@ class ClientArgs(
try { try {
client.runLoop() client.runLoop()
} catch (e: Exception) { } catch (e: Throwable) {
Runtime.getRuntime().removeShutdownHook( Runtime.getRuntime().removeShutdownHook(
hook hook
) )

View file

@ -189,7 +189,7 @@ class ServerManager(
val state = this.state as Running val state = this.state as Running
val newSettings = backendApi.pingControl(state.settings) val newSettings = backendApi.pingControl(state.settings)
if (newSettings != null) { if (newSettings is RemoteSettings) {
LOGGER.info { "Server settings received: $newSettings" } LOGGER.info { "Server settings received: $newSettings" }
warnBasedOnSettings(newSettings) warnBasedOnSettings(newSettings)
@ -201,7 +201,7 @@ class ServerManager(
} }
} }
} else { } else {
LOGGER.info { "Server ping failed - ignoring" } LOGGER.info { "Ignoring failed server ping - $newSettings" }
} }
} }
@ -209,7 +209,9 @@ class ServerManager(
this.state as Uninitialized this.state as Uninitialized
val remoteSettings = backendApi.loginToControl() val remoteSettings = backendApi.loginToControl()
?: throw RuntimeException("Failed to get a login response from server") if (remoteSettings !is RemoteSettings) {
throw RuntimeException(remoteSettings.toString())
}
LOGGER.info { "Server settings received: $remoteSettings" } LOGGER.info { "Server settings received: $remoteSettings" }
warnBasedOnSettings(remoteSettings) warnBasedOnSettings(remoteSettings)

View file

@ -40,6 +40,7 @@ data class ServerSettings(
val maxKilobitsPerSecond: Long = 0, val maxKilobitsPerSecond: Long = 0,
val externalMaxKilobitsPerSecond: Long = 0, val externalMaxKilobitsPerSecond: Long = 0,
val maxMebibytesPerHour: Long = 0, val maxMebibytesPerHour: Long = 0,
val externalIp: String? = null,
val port: Int = 443, val port: Int = 443,
val threads: Int = 0, val threads: Int = 0,
) )

View file

@ -23,6 +23,14 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming
import dev.afanasev.sekret.Secret import dev.afanasev.sekret.Secret
import org.http4k.core.Uri import org.http4k.core.Uri
sealed class PingResult
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class PingFailure(
val status: Int,
val error: String,
) : PingResult()
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class RemoteSettings( data class RemoteSettings(
val imageServer: Uri, val imageServer: Uri,
@ -33,7 +41,7 @@ data class RemoteSettings(
val paused: Boolean, val paused: Boolean,
val forceDisableTokens: Boolean = false, val forceDisableTokens: Boolean = false,
val tls: TlsCert? val tls: TlsCert?
) { ) : PingResult() {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (javaClass != other?.javaClass) return false if (javaClass != other?.javaClass) return false

View file

@ -0,0 +1,39 @@
/*
Mangadex@Home
Copyright (c) 2020, MangaDex Network
This file is part of MangaDex@Home.
MangaDex@Home is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
MangaDex@Home is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this MangaDex@Home. If not, see <http://www.gnu.org/licenses/>.
*/
package mdnet.settings
import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.databind.annotation.JsonNaming
import dev.afanasev.sekret.Secret
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class SettingsRequest(
@field:Secret val secret: String,
val ipAddress: String?,
val port: Int,
val diskSpace: Long,
val networkSpeed: Long,
val buildVersion: Int,
val tlsCreatedAt: String?,
)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class LogoutRequest(
@field:Secret val secret: String,
)