mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-06-30 17:34:39 -05:00
Localize WebView and Login pages (#1522)
* Localize WebView and Login pages * Switch to JTE for page rendering * Lint * Add gradle task dependency * JTE -> KTE * ShouldRunAfter * I guess we must --------- Co-authored-by: Syer10 <syer10@users.noreply.github.com>
This commit is contained in:
@@ -11,6 +11,7 @@ plugins {
|
|||||||
alias(libs.plugins.download)
|
alias(libs.plugins.download)
|
||||||
alias(libs.plugins.kotlin.multiplatform) apply false
|
alias(libs.plugins.kotlin.multiplatform) apply false
|
||||||
alias(libs.plugins.moko) apply false
|
alias(libs.plugins.moko) apply false
|
||||||
|
alias(libs.plugins.jte) apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ coroutines = "1.10.2"
|
|||||||
serialization = "1.9.0"
|
serialization = "1.9.0"
|
||||||
okhttp = "5.1.0" # Major version is locked by Tachiyomi extensions
|
okhttp = "5.1.0" # Major version is locked by Tachiyomi extensions
|
||||||
javalin = "6.7.0"
|
javalin = "6.7.0"
|
||||||
|
jte = "3.2.1"
|
||||||
jackson = "2.18.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
|
jackson = "2.18.3" # jackson version locked by javalin, ref: `io.javalin.core.util.OptionalDependency`
|
||||||
exposed = "0.61.0"
|
exposed = "0.61.0"
|
||||||
dex2jar = "v64" # Stuck until https://github.com/ThexXTURBOXx/dex2jar/issues/27 is fixed
|
dex2jar = "v64" # Stuck until https://github.com/ThexXTURBOXx/dex2jar/issues/27 is fixed
|
||||||
@@ -49,9 +50,12 @@ okio = "com.squareup.okio:okio:3.15.0"
|
|||||||
# Javalin api
|
# Javalin api
|
||||||
javalin-core = { module = "io.javalin:javalin", version.ref = "javalin" }
|
javalin-core = { module = "io.javalin:javalin", version.ref = "javalin" }
|
||||||
javalin-openapi = { module = "io.javalin:javalin-openapi", version.ref = "javalin" }
|
javalin-openapi = { module = "io.javalin:javalin-openapi", version.ref = "javalin" }
|
||||||
|
javalin-rendering = { module = "io.javalin:javalin-rendering", version.ref = "javalin" }
|
||||||
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
|
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
|
||||||
jackson-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
|
jackson-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
|
||||||
jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" }
|
jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" }
|
||||||
|
jte = { module = "gg.jte:jte", version.ref = "jte" }
|
||||||
|
kte = { module = "gg.jte:jte-kotlin", version.ref = "jte" }
|
||||||
|
|
||||||
# GraphQL
|
# GraphQL
|
||||||
graphql-kotlin-server = { module = "com.expediagroup:graphql-kotlin-server", version.ref = "graphqlkotlin" }
|
graphql-kotlin-server = { module = "com.expediagroup:graphql-kotlin-server", version.ref = "graphqlkotlin" }
|
||||||
@@ -177,6 +181,9 @@ shadowjar = { id = "com.github.johnrengelman.shadow", version = "8.1.1"}
|
|||||||
# Moko
|
# Moko
|
||||||
moko = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko" }
|
moko = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko" }
|
||||||
|
|
||||||
|
# JTE
|
||||||
|
jte = { id = "gg.jte.gradle", version.ref = "jte" }
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
shared = [
|
shared = [
|
||||||
"kotlin-stdlib-jdk8",
|
"kotlin-stdlib-jdk8",
|
||||||
@@ -216,6 +223,8 @@ okhttp = [
|
|||||||
javalin = [
|
javalin = [
|
||||||
"javalin-core",
|
"javalin-core",
|
||||||
#"javalin-openapi",
|
#"javalin-openapi",
|
||||||
|
"javalin-rendering",
|
||||||
|
"jte",
|
||||||
]
|
]
|
||||||
jackson = [
|
jackson = [
|
||||||
"jackson-databind",
|
"jackson-databind",
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ plugins {
|
|||||||
.get()
|
.get()
|
||||||
.pluginId,
|
.pluginId,
|
||||||
)
|
)
|
||||||
|
id(
|
||||||
|
libs.plugins.jte
|
||||||
|
.get()
|
||||||
|
.pluginId,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -96,6 +101,12 @@ dependencies {
|
|||||||
implementation(libs.cron4j)
|
implementation(libs.cron4j)
|
||||||
|
|
||||||
implementation(libs.cronUtils)
|
implementation(libs.cronUtils)
|
||||||
|
|
||||||
|
compileOnly(libs.kte)
|
||||||
|
}
|
||||||
|
|
||||||
|
jte {
|
||||||
|
generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
@@ -212,4 +223,8 @@ tasks {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runKtlintCheckOverMainSourceSet {
|
||||||
|
mustRunAfter(generateJte)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,4 +80,23 @@
|
|||||||
<string name="manga_status_publishing_finished">Publishing Finished</string>
|
<string name="manga_status_publishing_finished">Publishing Finished</string>
|
||||||
<string name="manga_status_cancelled">Cancelled</string>
|
<string name="manga_status_cancelled">Cancelled</string>
|
||||||
<string name="manga_status_on_hiatus">On Hiatus</string>
|
<string name="manga_status_on_hiatus">On Hiatus</string>
|
||||||
|
|
||||||
|
<string name="label_error">Error</string>
|
||||||
|
<string name="label_version">Version <xliff:g id="version" example="v2.0.1833">%1$s</xliff:g></string>
|
||||||
|
|
||||||
|
<string name="webview_label_title">Suwayomi WebView</string>
|
||||||
|
<string name="webview_label_disconnected">Disconnected, please refresh</string>
|
||||||
|
<string name="webview_label_reversescroll">Reverse Scrolling</string>
|
||||||
|
<string name="webview_label_bindingshint">Note: While focus is on the WebView part, no keybinds, including refresh, will be handled by the browser.</string>
|
||||||
|
<string name="webview_label_init">Initializing... Please wait</string>
|
||||||
|
<string name="webview_label_getstarted">Enter a URL to get started</string>
|
||||||
|
<string name="webview_label_loading">Loading page...</string>
|
||||||
|
<string name="webview_placeholder_url">Enter URL...</string>
|
||||||
|
|
||||||
|
<string name="login_label_title">Suwayomi Login</string>
|
||||||
|
<string name="login_label_username">Username</string>
|
||||||
|
<string name="login_label_password">Password</string>
|
||||||
|
<string name="login_label_login">Log In</string>
|
||||||
|
<string name="login_placeholder_username">Type username...</string>
|
||||||
|
<string name="login_placeholder_password">Secret...</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
|
@import suwayomi.tachidesk.i18n.MR
|
||||||
|
@import suwayomi.tachidesk.server.generated.BuildConfig
|
||||||
|
|
||||||
|
@param locale: java.util.Locale
|
||||||
|
@param error: String
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Suwayomi Login</title>
|
<title>${MR.strings.login_label_title.localized(locale)}</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -143,22 +149,22 @@
|
|||||||
<h1>Suwayomi</h1>
|
<h1>Suwayomi</h1>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<div class="error">[ERROR]</div>
|
<div class="error">${error}</div>
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<h2>Login</h2>
|
<h2>Login</h2>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label for="user">Username:</label>
|
<label for="user">${MR.strings.login_label_username.localized(locale)}:</label>
|
||||||
<input type="text" name="user" id="user" required placeholder="Type username..."/>
|
<input type="text" name="user" id="user" required placeholder="${MR.strings.login_placeholder_username.localized(locale)}"/>
|
||||||
<label for="pass">Password:</label>
|
<label for="pass">${MR.strings.login_label_password.localized(locale)}:</label>
|
||||||
<input type="password" name="pass" id="pass" required placeholder="Secret..."/>
|
<input type="password" name="pass" id="pass" required placeholder="${MR.strings.login_placeholder_password.localized(locale)}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="submit">
|
<div class="submit">
|
||||||
<button type="submit">Log In</button>
|
<button type="submit">${MR.strings.login_label_login.localized(locale)}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<p>Suwayomi: Version [VERSION]</p>
|
<p>Suwayomi: ${MR.strings.label_version.localized(locale, BuildConfig.VERSION)}</p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
|
@import suwayomi.tachidesk.i18n.MR
|
||||||
|
|
||||||
|
@param locale: java.util.Locale
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content" />
|
||||||
<title>Suwayomi Webview</title>
|
<title>${MR.strings.webview_label_title.localized(locale)}</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -20,7 +24,7 @@
|
|||||||
letter-spacing: 0em;
|
letter-spacing: 0em;
|
||||||
}
|
}
|
||||||
body.disconnected::after {
|
body.disconnected::after {
|
||||||
content: 'Disconnected, please refresh';
|
content: "${MR.strings.webview_label_disconnected.localized(locale)}";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(150, 0, 0, 0.5);
|
background: rgba(150, 0, 0, 0.5);
|
||||||
@@ -141,18 +145,18 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<h1 id="title">Suwayomi: WebView</h1>
|
<h1 id="title">${MR.strings.webview_label_title.localized(locale)}</h1>
|
||||||
<nav>
|
<nav>
|
||||||
<form id="browseForm">
|
<form id="browseForm">
|
||||||
<input type="text" id="url" name="url" placeholder="Enter URL..." disabled/>
|
<input type="text" id="url" name="url" placeholder="${MR.strings.webview_placeholder_url.localized(locale)}" disabled/>
|
||||||
<button type="submit" id="goButton" disabled><span class="arrow-right"></span></button>
|
<button type="submit" id="goButton" disabled><span class="arrow-right"></span></button>
|
||||||
</form>
|
</form>
|
||||||
<label><input type="checkbox" id="reverseScroll" disabled/> Reverse Scrolling</label>
|
<label><input type="checkbox" id="reverseScroll" disabled/> ${MR.strings.webview_label_reversescroll.localized(locale)}</label>
|
||||||
</nav>
|
</nav>
|
||||||
<p><i>Note: While focus is on the WebView part, no keybinds, including refresh, will be handled by the browser</i></p>
|
<p><i>${MR.strings.webview_label_bindingshint.localized(locale)}</i></p>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<div class="message" id="message">Initializing... Please wait</div>
|
<div class="message" id="message">${MR.strings.webview_label_init.localized(locale)}</div>
|
||||||
<div class="status" id="status"></div>
|
<div class="status" id="status"></div>
|
||||||
<canvas id="frame"></canvas>
|
<canvas id="frame"></canvas>
|
||||||
<input type="text" id="inputtrap" autocomplete="off"/>
|
<input type="text" id="inputtrap" autocomplete="off"/>
|
||||||
@@ -168,6 +172,7 @@
|
|||||||
const urlInput = document.getElementById('url');
|
const urlInput = document.getElementById('url');
|
||||||
const titleDiv = document.getElementById('title');
|
const titleDiv = document.getElementById('title');
|
||||||
const reverseToggle = document.getElementById('reverseScroll');
|
const reverseToggle = document.getElementById('reverseScroll');
|
||||||
|
const origTitle = document.title;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const socketUrl = (window.location.origin + window.location.pathname).replace(/^http/,'ws');
|
const socketUrl = (window.location.origin + window.location.pathname).replace(/^http/,'ws');
|
||||||
@@ -200,8 +205,8 @@
|
|||||||
|
|
||||||
const setTitle = (title) => {
|
const setTitle = (title) => {
|
||||||
if (!title) {
|
if (!title) {
|
||||||
document.title = "Suwayomi Webview";
|
document.title = origTitle;
|
||||||
titleDiv.textContent = "Suwayomi Webview";
|
titleDiv.textContent = origTitle;
|
||||||
} else {
|
} else {
|
||||||
document.title = "Suwayomi: " + title;
|
document.title = "Suwayomi: " + title;
|
||||||
titleDiv.textContent = "Suwayomi: " + title;
|
titleDiv.textContent = "Suwayomi: " + title;
|
||||||
@@ -213,11 +218,11 @@
|
|||||||
urlInput.value = u;
|
urlInput.value = u;
|
||||||
setHash(u);
|
setHash(u);
|
||||||
setTitle();
|
setTitle();
|
||||||
messageDiv.textContent = 'Enter a URL to get started';
|
messageDiv.textContent = "${MR.strings.webview_label_getstarted.localized(locale)}";
|
||||||
ctx.clearRect(0, 0, frame.width, frame.height);
|
ctx.clearRect(0, 0, frame.width, frame.height);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
messageDiv.textContent = "Loading page...";
|
messageDiv.textContent = "${MR.strings.webview_label_loading.localized(locale)}";
|
||||||
messageDiv.classList.remove('error');
|
messageDiv.classList.remove('error');
|
||||||
urlInput.value = u;
|
urlInput.value = u;
|
||||||
socket.send(JSON.stringify({ type: 'loadUrl', url: u, width: frame.clientWidth, height: frame.clientHeight }));
|
socket.send(JSON.stringify({ type: 'loadUrl', url: u, width: frame.clientWidth, height: frame.clientHeight }));
|
||||||
@@ -265,7 +270,7 @@
|
|||||||
break;
|
break;
|
||||||
case "load": {
|
case "load": {
|
||||||
if (obj.error) {
|
if (obj.error) {
|
||||||
messageDiv.textContent = "Error: " + obj.error;
|
messageDiv.textContent = "${MR.strings.label_error.localized(locale)}: " + obj.error;
|
||||||
messageDiv.classList.add('error');
|
messageDiv.classList.add('error');
|
||||||
} else {
|
} else {
|
||||||
messageDiv.textContent = "";
|
messageDiv.textContent = "";
|
||||||
@@ -286,7 +291,7 @@
|
|||||||
} break;
|
} break;
|
||||||
case "consoleMessage": {
|
case "consoleMessage": {
|
||||||
const lg = obj.severity == 4 ? console.error : obj.severity == 3 ? console.warn : console.log;
|
const lg = obj.severity == 4 ? console.error : obj.severity == 3 ? console.warn : console.log;
|
||||||
lg(`${obj.source}:${obj.line}:`, obj.message);
|
lg(obj.source + ':' + obj.line + ':', obj.message);
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
console.warn("Unknown event", obj.type)
|
console.warn("Unknown event", obj.type)
|
||||||
@@ -296,7 +301,7 @@
|
|||||||
|
|
||||||
socket.addEventListener('close', e => {
|
socket.addEventListener('close', e => {
|
||||||
if (e.wasClean) {
|
if (e.wasClean) {
|
||||||
console.log(`WebSocket connection closed cleanly, code=${e.code}, reason=${e.reason}`);
|
console.log(`WebSocket connection closed cleanly, code=` + e.code + `, reason=` + e.reason);
|
||||||
} else {
|
} else {
|
||||||
console.error('WebSocket connection died');
|
console.error('WebSocket connection died');
|
||||||
}
|
}
|
||||||
@@ -304,7 +309,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener('error', e => {
|
socket.addEventListener('error', e => {
|
||||||
messageDiv.textContent = "Error: " + (e.message || e.reason || e);
|
messageDiv.textContent = "${MR.strings.label_error.localized(locale)}: " + (e.message || e.reason || e);
|
||||||
messageDiv.classList.add('error');
|
messageDiv.classList.add('error');
|
||||||
console.error('WebSocket error:', e);
|
console.error('WebSocket error:', e);
|
||||||
});
|
});
|
||||||
@@ -417,7 +422,7 @@
|
|||||||
attachEvents();
|
attachEvents();
|
||||||
frameInput.focus();
|
frameInput.focus();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
messageDiv.textContent = "Error: " + (e.message || e);
|
messageDiv.textContent = "${MR.strings.label_error.localized(locale)}: " + (e.message || e);
|
||||||
messageDiv.classList.add('error');
|
messageDiv.classList.add('error');
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
@@ -11,21 +11,31 @@ import io.javalin.http.ContentType
|
|||||||
import io.javalin.http.HttpStatus
|
import io.javalin.http.HttpStatus
|
||||||
import io.javalin.websocket.WsConfig
|
import io.javalin.websocket.WsConfig
|
||||||
import suwayomi.tachidesk.global.impl.WebView
|
import suwayomi.tachidesk.global.impl.WebView
|
||||||
|
import suwayomi.tachidesk.i18n.LocalizationHelper
|
||||||
import suwayomi.tachidesk.server.util.handler
|
import suwayomi.tachidesk.server.util.handler
|
||||||
|
import suwayomi.tachidesk.server.util.queryParam
|
||||||
import suwayomi.tachidesk.server.util.withOperation
|
import suwayomi.tachidesk.server.util.withOperation
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
object WebViewController {
|
object WebViewController {
|
||||||
val webview =
|
val webview =
|
||||||
handler(
|
handler(
|
||||||
|
queryParam<String?>("lang"),
|
||||||
documentWith = {
|
documentWith = {
|
||||||
withOperation {
|
withOperation {
|
||||||
summary("WebView")
|
summary("WebView")
|
||||||
description("Opens and browses WebView")
|
description("Opens and browses WebView")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx, lang ->
|
||||||
|
val locale: Locale = LocalizationHelper.ctxToLocale(ctx, lang)
|
||||||
ctx.contentType(ContentType.TEXT_HTML)
|
ctx.contentType(ContentType.TEXT_HTML)
|
||||||
ctx.result(javaClass.getResourceAsStream("/webview.html")!!)
|
ctx.render(
|
||||||
|
"Webview.jte",
|
||||||
|
mapOf(
|
||||||
|
"locale" to locale,
|
||||||
|
),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
withResults = { mime<String>(HttpStatus.OK, "text/html") },
|
withResults = { mime<String>(HttpStatus.OK, "text/html") },
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ package suwayomi.tachidesk.server
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import gg.jte.ContentType
|
||||||
|
import gg.jte.TemplateEngine
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import io.javalin.Javalin
|
import io.javalin.Javalin
|
||||||
import io.javalin.apibuilder.ApiBuilder.path
|
import io.javalin.apibuilder.ApiBuilder.path
|
||||||
@@ -15,6 +17,7 @@ import io.javalin.http.HttpStatus
|
|||||||
import io.javalin.http.RedirectResponse
|
import io.javalin.http.RedirectResponse
|
||||||
import io.javalin.http.UnauthorizedResponse
|
import io.javalin.http.UnauthorizedResponse
|
||||||
import io.javalin.http.staticfiles.Location
|
import io.javalin.http.staticfiles.Location
|
||||||
|
import io.javalin.rendering.template.JavalinJte
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
@@ -25,14 +28,15 @@ import org.eclipse.jetty.server.ServerConnector
|
|||||||
import suwayomi.tachidesk.global.GlobalAPI
|
import suwayomi.tachidesk.global.GlobalAPI
|
||||||
import suwayomi.tachidesk.graphql.GraphQL
|
import suwayomi.tachidesk.graphql.GraphQL
|
||||||
import suwayomi.tachidesk.graphql.types.AuthMode
|
import suwayomi.tachidesk.graphql.types.AuthMode
|
||||||
|
import suwayomi.tachidesk.i18n.LocalizationHelper
|
||||||
import suwayomi.tachidesk.manga.MangaAPI
|
import suwayomi.tachidesk.manga.MangaAPI
|
||||||
import suwayomi.tachidesk.opds.OpdsAPI
|
import suwayomi.tachidesk.opds.OpdsAPI
|
||||||
import suwayomi.tachidesk.server.generated.BuildConfig
|
|
||||||
import suwayomi.tachidesk.server.util.Browser
|
import suwayomi.tachidesk.server.util.Browser
|
||||||
import suwayomi.tachidesk.server.util.WebInterfaceManager
|
import suwayomi.tachidesk.server.util.WebInterfaceManager
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
|
import java.util.Locale
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
||||||
@@ -49,6 +53,8 @@ object JavalinSetup {
|
|||||||
fun javalinSetup() {
|
fun javalinSetup() {
|
||||||
val app =
|
val app =
|
||||||
Javalin.create { config ->
|
Javalin.create { config ->
|
||||||
|
val templateEngine = TemplateEngine.createPrecompiled(ContentType.Html)
|
||||||
|
config.fileRenderer(JavalinJte(templateEngine))
|
||||||
if (serverConfig.webUIEnabled.value) {
|
if (serverConfig.webUIEnabled.value) {
|
||||||
val serveWebUI = {
|
val serveWebUI = {
|
||||||
config.spaRoot.addFile("/", applicationDirs.webUIRoot + "/index.html", Location.EXTERNAL)
|
config.spaRoot.addFile("/", applicationDirs.webUIRoot + "/index.html", Location.EXTERNAL)
|
||||||
@@ -118,16 +124,17 @@ object JavalinSetup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.get("/login.html") { ctx ->
|
app.get("/login.html") { ctx ->
|
||||||
var page =
|
val locale: Locale = LocalizationHelper.ctxToLocale(ctx)
|
||||||
this::class.java
|
|
||||||
.getResourceAsStream("/static/login.html")!!
|
|
||||||
.use { it.readAllBytes() }
|
|
||||||
.toString(Charsets.UTF_8)
|
|
||||||
page = page.replace("[VERSION]", BuildConfig.VERSION).replace("[ERROR]", "")
|
|
||||||
ctx.header("content-type", "text/html")
|
ctx.header("content-type", "text/html")
|
||||||
val httpCacheSeconds = 1.days.inWholeSeconds
|
val httpCacheSeconds = 1.days.inWholeSeconds
|
||||||
ctx.header("cache-control", "max-age=$httpCacheSeconds")
|
ctx.header("cache-control", "max-age=$httpCacheSeconds")
|
||||||
ctx.result(page)
|
ctx.render(
|
||||||
|
"Login.jte",
|
||||||
|
mapOf(
|
||||||
|
"locale" to locale,
|
||||||
|
"error" to "",
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.post("/login.html") { ctx ->
|
app.post("/login.html") { ctx ->
|
||||||
@@ -147,15 +154,16 @@ object JavalinSetup {
|
|||||||
throw RedirectResponse(HttpStatus.SEE_OTHER)
|
throw RedirectResponse(HttpStatus.SEE_OTHER)
|
||||||
}
|
}
|
||||||
|
|
||||||
var page =
|
val locale: Locale = LocalizationHelper.ctxToLocale(ctx)
|
||||||
this::class.java
|
|
||||||
.getResourceAsStream("/static/login.html")!!
|
|
||||||
.use { it.readAllBytes() }
|
|
||||||
.toString(Charsets.UTF_8)
|
|
||||||
page = page.replace("[VERSION]", BuildConfig.VERSION).replace("[ERROR]", "Invalid username or password")
|
|
||||||
ctx.header("content-type", "text/html")
|
ctx.header("content-type", "text/html")
|
||||||
ctx.req().session.invalidate()
|
ctx.req().session.invalidate()
|
||||||
ctx.result(page)
|
ctx.render(
|
||||||
|
"Login.jte",
|
||||||
|
mapOf(
|
||||||
|
"locale" to locale,
|
||||||
|
"error" to "Invalid username or password",
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.beforeMatched { ctx ->
|
app.beforeMatched { ctx ->
|
||||||
|
|||||||
Reference in New Issue
Block a user