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

Remove webui

This commit is contained in:
carbotaniuman 2021-01-10 15:30:41 -06:00
parent 821cc85776
commit 76cf90e31d
37 changed files with 55 additions and 560 deletions

View file

@ -41,6 +41,14 @@ dependencies {
implementation "info.picocli:picocli:4.5.0"
kapt "info.picocli:picocli-codegen:4.5.0"
testImplementation "io.kotest:kotest-runner-junit5:$kotest_version"
testImplementation "io.kotest:kotest-assertions-core:$kotest_version"
}
test {
useJUnitPlatform()
}
kapt {

View file

@ -1,2 +1,3 @@
http_4k_version=3.284.0
exposed_version=0.26.2
exposed_version=0.26.2
kotest_version=4.4.0.RC1

View file

@ -28,9 +28,7 @@ import com.fasterxml.jackson.module.kotlin.readValue
import mdnet.base.Main.dieWithError
import mdnet.base.cache.ImageStorage
import mdnet.base.logging.*
import mdnet.base.server.getUiServer
import mdnet.base.settings.*
import org.http4k.server.Http4kServer
import org.ktorm.database.Database
import org.slf4j.LoggerFactory
import java.io.File
@ -57,7 +55,6 @@ class MangaDexClient(private val settingsFile: File, databaseFile: File, cacheFo
// state that must only be accessed from the thread on the executor
private var imageServer: ServerManager? = null
private var webUi: Http4kServer? = null
// end protected state
init {
@ -99,23 +96,6 @@ class MangaDexClient(private val settingsFile: File, databaseFile: File, cacheFo
)
startImageServer()
startWebUi()
}
// Precondition: settings must be filled with up-to-date settings and `imageServer` must not be null
private fun startWebUi() {
settings.webSettings?.let { webSettings ->
val imageServer = requireNotNull(imageServer)
if (webUi != null) {
throw AssertionError()
}
LOGGER.info { "WebUI starting" }
webUi = getUiServer(webSettings, imageServer.statistics, imageServer.statsMap).also {
it.start()
}
LOGGER.info { "WebUI started" }
}
}
// Precondition: settings must be filled with up-to-date settings
@ -137,13 +117,6 @@ class MangaDexClient(private val settingsFile: File, databaseFile: File, cacheFo
LOGGER.info { "Server manager stopped" }
}
private fun stopWebUi() {
LOGGER.info { "WebUI stopping" }
requireNotNull(webUi).stop()
webUi = null
LOGGER.info { "WebUI stopped" }
}
fun shutdown() {
LOGGER.info { "Mangadex@Home Client shutting down" }
val latch = CountDownLatch(1)
@ -152,15 +125,11 @@ class MangaDexClient(private val settingsFile: File, databaseFile: File, cacheFo
executor.schedule(
{
if (webUi != null) {
stopWebUi()
}
if (imageServer != null) {
stopImageServer()
}
storage.close()
latch.countDown()
},
0, TimeUnit.SECONDS
@ -193,25 +162,11 @@ class MangaDexClient(private val settingsFile: File, databaseFile: File, cacheFo
val restartServer = newSettings.serverSettings != settings.serverSettings ||
newSettings.devSettings != settings.devSettings
val stopWebUi = restartServer || newSettings.webSettings != settings.webSettings
val startWebUi = stopWebUi && newSettings.webSettings != null
if (stopWebUi) {
LOGGER.info { "Stopping WebUI to reload ClientSettings" }
if (webUi != null) {
stopWebUi()
}
}
if (restartServer) {
stopImageServer()
startImageServer()
}
if (startWebUi) {
startWebUi()
}
settings = newSettings
} catch (e: UnrecognizedPropertyException) {
LOGGER.warn { "Settings file is invalid: '$e.propertyName' is not a valid setting" }
@ -256,11 +211,6 @@ class MangaDexClient(private val settingsFile: File, databaseFile: File, cacheFo
throw ClientSettingsException("Config Error: Graceful shutdown wait must be >= 15")
}
}
settings.webSettings?.let {
if (it.port == 0) {
throw ClientSettingsException("Config Error: Invalid UI port number")
}
}
}
private fun readClientSettings(): ClientSettings {

View file

@ -132,10 +132,7 @@ class ServerHandler(
}
private fun getServerAddress(): String {
return if (!devSettings.isDev)
SERVER_ADDRESS
else
SERVER_ADDRESS_DEV
return devSettings.devUrl ?: SERVER_ADDRESS
}
companion object {
@ -143,6 +140,5 @@ class ServerHandler(
private val STRING_ANY_MAP_LENS = Body.auto<Map<String, Any>>().toLens()
private val SERVER_SETTINGS_LENS = Body.auto<RemoteSettings>().toLens()
private const val SERVER_ADDRESS = "https://api.mangadex.network/"
private const val SERVER_ADDRESS_DEV = "https://mangadex-test.net/"
}
}

View file

@ -49,7 +49,7 @@ data class Image(val data: ImageMetadata, val stream: InputStream)
*
* @constructor Creates an `ImageStorage`, creating necessary tables in the database.
*/
class ImageStorage(var maxSize: Long, private val cacheDirectory: Path, private val database: Database) {
class ImageStorage(var maxSize: Long, private val cacheDirectory: Path, private val database: Database, autoPrune: Boolean = true) {
private val evictor: ScheduledExecutorService = Executors.newScheduledThreadPool(2)
private val queue = LinkedBlockingQueue<String>()
/**
@ -71,7 +71,6 @@ class ImageStorage(var maxSize: Long, private val cacheDirectory: Path, private
// clear any files that have been writing or reading
// and are still in the database for some reason
// FIXME: also run this regularly based on access times
val iter = database.from(DbImage)
.select(DbImage.id)
.where { DbImage.lock notEq DbImageLock.UNLOCKED }
@ -109,40 +108,51 @@ class ImageStorage(var maxSize: Long, private val cacheDirectory: Path, private
1, 1, TimeUnit.MINUTES
)
evictor.scheduleWithFixedDelay(
{
size = calculateSize()
if (autoPrune) {
evictor.scheduleWithFixedDelay(
{
pruneImages()
},
0, 3, TimeUnit.MINUTES
)
}
}
LOGGER.info { "Cache at $size out of $maxSize bytes" }
// we need to prune the cache now
if (size > maxSize * 0.95) {
val toClear = size - (maxSize * 0.9).toLong()
LOGGER.info { "Evicting at least $toClear bytes from cache" }
/**
* Prunes excess images from the cache in order to meet
* the [maxSize] property and not waste disk space.
*/
fun pruneImages() {
val size = calculateSize()
this.size = size
val list = database.useConnection { conn ->
conn.prepareStatement(IMAGES_TO_PRUNE).apply {
setLong(1, toClear)
}.use { stmt ->
stmt.executeQuery().let {
val ret = ArrayList<String>()
LOGGER.info { "Cache at $size out of $maxSize bytes" }
// we need to prune the cache now
if (size > maxSize * 0.95) {
val toClear = size - (maxSize * 0.9).toLong()
LOGGER.info { "Evicting at least $toClear bytes from cache" }
while (it.next()) {
ret.add(it.getString(1))
}
val list = database.useConnection { conn ->
conn.prepareStatement(IMAGES_TO_PRUNE).apply {
setLong(1, toClear)
}.use { stmt ->
stmt.executeQuery().let {
val ret = ArrayList<String>()
ret
}
while (it.next()) {
ret.add(it.getString(1))
}
}
for (id in list) {
LOGGER.info { "Evicting images $id from cache" }
deleteImage(id)
ret
}
}
},
0, 3, TimeUnit.MINUTES
)
}
for (id in list) {
LOGGER.info { "Evicting images $id from cache" }
deleteImage(id)
}
}
}
/**

View file

@ -116,9 +116,8 @@ class Netty(private val tls: TlsCert, private val serverSettings: ServerSettings
LOGGER.info { "User (downloader) abruptly closed the connection" }
LOGGER.trace(cause) { "Exception in pipeline" }
} else if (cause !is ReadTimeoutException && cause !is WriteTimeoutException) {
// ctx.fireExceptionCaught(cause)
ctx.fireExceptionCaught(cause)
}
ctx.fireExceptionCaught(cause)
}
}
)

View file

@ -83,11 +83,6 @@ class ImageServer(
"/data"
} + "/$chapterHash/$fileName"
if (!request.referrerMatches(ALLOWED_REFERER_DOMAINS)) {
LOGGER.info { "Request for $sanitizedUri rejected due to non-allowed referrer ${request.header("Referer")}" }
return@then Response(Status.FORBIDDEN)
}
if (remoteSettings.forceTokens && !isTestImage(chapterHash)) {
val tokenArr = try {
val toDecode = try {
@ -152,21 +147,6 @@ class ImageServer(
}
}
/**
* Filters referrers based on passed (sub)domains. Ignores `scheme` (protocol) in URL
*/
private fun Request.referrerMatches(allowedDomains: List<String>, permitBlank: Boolean = true): Boolean {
val referer = this.header("Referer") ?: return permitBlank // Referrer was misspelled as "Referer" and now we're stuck with it -_-
if (referer == "") return permitBlank
return allowedDomains.any {
referer.substringAfter("//") // Ignore scheme
.substringBefore("/") // Ignore path
.substringBefore(":")
.endsWith(it)
}
}
private fun Request.handleCacheHit(sanitizedUri: String, image: Image): Response {
// our files never change, so it's safe to use the browser cache
return if (this.header("If-Modified-Since") != null) {
@ -283,7 +263,6 @@ class ImageServer(
private val JACKSON: ObjectMapper = jacksonObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.registerModule(JavaTimeModule())
private val ALLOWED_REFERER_DOMAINS = listOf("mangadex.org", "mangadex.network") // TODO: Factor out hardcoded domains?
private fun baseHandler(): Filter =
CachingFilters.Response.MaxAge(Clock.systemUTC(), Constants.MAX_AGE_CACHE)

View file

@ -1,65 +0,0 @@
/*
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/>.
*/
/* ktlint-disable no-wildcard-imports */
package mdnet.base.server
import mdnet.base.data.Statistics
import mdnet.base.netty.WebUiNetty
import mdnet.base.settings.WebSettings
import org.http4k.core.Body
import org.http4k.core.Method
import org.http4k.core.Response
import org.http4k.core.Status
import org.http4k.core.then
import org.http4k.filter.ServerFilters
import org.http4k.format.Jackson.auto
import org.http4k.routing.ResourceLoader
import org.http4k.routing.bind
import org.http4k.routing.routes
import org.http4k.routing.singlePageApp
import org.http4k.server.Http4kServer
import org.http4k.server.asServer
import java.time.Instant
import java.util.concurrent.atomic.AtomicReference
fun getUiServer(
webSettings: WebSettings,
statistics: AtomicReference<Statistics>,
statsMap: Map<Instant, Statistics>
): Http4kServer {
val statsMapLens = Body.auto<Map<Instant, Statistics>>().toLens()
return catchAllHideDetails()
.then(ServerFilters.CatchLensFailure)
.then(addCommonHeaders())
.then(
routes(
"/api/stats" bind Method.GET to {
statsMapLens(mapOf(Instant.now() to statistics.get()), Response(Status.OK))
},
"/api/pastStats" bind Method.GET to {
synchronized(statsMap) {
statsMapLens(statsMap, Response(Status.OK))
}
},
singlePageApp(ResourceLoader.Classpath("/webui"))
)
)
.asServer(WebUiNetty(webSettings.hostname, webSettings.port))
}

View file

@ -25,9 +25,8 @@ import dev.afanasev.sekret.Secret
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class ClientSettings(
val maxCacheSizeInMebibytes: Long = 20480,
val devSettings: DevSettings = DevSettings(isDev = false),
val devSettings: DevSettings = DevSettings(),
val serverSettings: ServerSettings = ServerSettings(),
val webSettings: WebSettings? = null,
)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
@ -42,13 +41,7 @@ data class ServerSettings(
val threads: Int = 4,
)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class WebSettings(
val hostname: String = "127.0.0.1",
val port: Int = 8080
)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class DevSettings(
val isDev: Boolean = false
val devUrl: String? = null
)

View file

@ -1,8 +1,8 @@
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">-->
<!-- <level>${file-level:-WARN}</level>-->
<!-- </filter>-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>${file-level:-WARN}</level>
</filter>
<file>log/latest.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
@ -32,7 +32,6 @@
<appender-ref ref="FILE"/>
</root>
<logger name="org." level="ERROR"/>
<logger name="io.netty" level="INFO"/>
<logger name="org.apache" level="WARN"/>
<logger name="org.apache.hc.client5" level="ERROR"/>

View file

@ -1 +0,0 @@
.vue-resizable-handle{background-image:none!important}.xterm{font-feature-settings:"liga" 0;position:relative;-moz-user-select:none;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm{cursor:text}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility,.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:.5}.xterm-underline{text-decoration:underline}.xterm ::-webkit-scrollbar{width:7px}.xterm ::-webkit-scrollbar-track{background-color:transparent}.xterm ::-webkit-scrollbar-thumb{background-color:#fff}

View file

@ -1 +0,0 @@
.echarts{width:600px;height:400px}

File diff suppressed because one or more lines are too long

View file

@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.00251 14.9297L0 1.07422H6.14651L8.00251 4.27503L9.84583 1.07422H16L8.00251 14.9297Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 215 B

View file

@ -1 +0,0 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="favicon.ico"><![endif]--><title>MD@H Client</title><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css><script src=https://cdn.jsdelivr.net/npm/echarts@4.1.0/dist/echarts.js></script><script src=https://cdn.jsdelivr.net/npm/vue-echarts@4.0.2></script><script src=https://unpkg.com/xterm@4.0.0/lib/xterm.js></script><link rel=text/css href=node_modules/xterm/css/xterm.css><link href=css/chunk-7577183e.6dc57fe0.css rel=prefetch><link href=js/chunk-7577183e.d6d29bcc.js rel=prefetch><link href=css/app.14a6e628.css rel=preload as=style><link href=css/chunk-vendors.b02cf67a.css rel=preload as=style><link href=js/app.ede7edb7.js rel=preload as=script><link href=js/chunk-vendors.1256013f.js rel=preload as=script><link href=css/chunk-vendors.b02cf67a.css rel=stylesheet><link href=css/app.14a6e628.css rel=stylesheet><link rel=icon type=image/png sizes=32x32 href=img/icons/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=img/icons/favicon-16x16.png><link rel=manifest href=manifest.json><meta name=theme-color content=#f79421><meta name=apple-mobile-web-app-capable content=yes><meta name=apple-mobile-web-app-status-bar-style content=black><meta name=apple-mobile-web-app-title content="MD@H Client Interface"><link rel=apple-touch-icon href=img/icons/apple-touch-icon-152x152.png><link rel=mask-icon href=img/icons/safari-pinned-tab.svg color=#f79421><meta name=msapplication-TileImage content=img/icons/msapplication-icon-144x144.png><meta name=msapplication-TileColor content=#000000></head><body style="overflow: hidden"><noscript><div style="background-color: #0980e8; position: absolute; top: 0; left: 0; width: 100%; height: 100%; user-select: none"><div style="position: absolute; top: 15%; left: 20%; width: 60%; font-family: Segoe UI; color: white;"><p style="font-size: 180px; margin: 0">:(</p><p style="font-size: 30px; margin-top: 50px">It appears that you don't have javascript enabled.<br>This isn't a big deal, but it just means that you've killed my wonderful web UI.<br>How evil of you...</p><p style="font-size: 10px; margin-top: 10px">Really though ;-;<br>I put in a lot of work and I'm very sad that you choose to disable the one thing that I needed :/</p></div></div></noscript><div id=app></div><script src=js/chunk-vendors.1256013f.js></script><script src=js/app.ede7edb7.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
{"name":"MD@H Client Interface","short_name":"MD@H","theme_color":"#f79421","icons":[{"src":"./img/icons/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"./img/icons/android-chrome-512x512.png","sizes":"512x512","type":"image/png"},{"src":"./img/icons/android-chrome-maskable-192x192.png","sizes":"192x192","type":"image/png","purpose":"maskable"},{"src":"./img/icons/android-chrome-maskable-512x512.png","sizes":"512x512","type":"image/png","purpose":"maskable"}],"start_url":"","display":"standalone","background_color":"#000000"}

View file

@ -1,38 +0,0 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "bb309837f2cf709edac5",
"url": "css/app.14a6e628.css"
},
{
"revision": "f9df31735412a9a525ef",
"url": "css/chunk-7577183e.6dc57fe0.css"
},
{
"revision": "027724770bde7ff56e58",
"url": "css/chunk-vendors.b02cf67a.css"
},
{
"revision": "7968686572fffa22fa9bdf28cc308706",
"url": "index.html"
},
{
"revision": "bb309837f2cf709edac5",
"url": "js/app.ede7edb7.js"
},
{
"revision": "f9df31735412a9a525ef",
"url": "js/chunk-7577183e.d6d29bcc.js"
},
{
"revision": "027724770bde7ff56e58",
"url": "js/chunk-vendors.1256013f.js"
},
{
"revision": "134416f208a045e960280cbf5c867e5c",
"url": "manifest.json"
},
{
"revision": "b6216d61c03e6ce0c9aea6ca7808f7ca",
"url": "robots.txt"
}
]);

View file

@ -1,2 +0,0 @@
User-agent: *
Disallow:

View file

@ -1,3 +0,0 @@
importScripts("precache-manifest.9917f0a006705c9b6b6c1abfab436c1f.js", "https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");