diff --git a/AndroidCompat/Config/src/main/java/xyz/nulldev/ts/config/ConfigManager.kt b/AndroidCompat/Config/src/main/java/xyz/nulldev/ts/config/ConfigManager.kt index 64b3441c..4f67ea3a 100644 --- a/AndroidCompat/Config/src/main/java/xyz/nulldev/ts/config/ConfigManager.kt +++ b/AndroidCompat/Config/src/main/java/xyz/nulldev/ts/config/ConfigManager.kt @@ -138,7 +138,7 @@ open class ConfigManager { * - adds missing settings * - removes outdated settings */ - fun updateUserConfig() { + fun updateUserConfig(migrate: ConfigDocument.(Config) -> ConfigDocument) { val serverConfig = ConfigFactory.parseResources("server-reference.conf") val userConfig = getUserConfig() @@ -162,7 +162,11 @@ open class ConfigManager { ) }.forEach { newUserConfigDoc = newUserConfigDoc.withValue(it.key, it.value) } + newUserConfigDoc = + migrate(newUserConfigDoc, internalConfig) + userConfigFile.writeText(newUserConfigDoc.render()) + getUserConfig().entrySet().forEach { internalConfig = internalConfig.withValue(it.key, it.value) } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/global/impl/WebView.kt b/server/src/main/kotlin/suwayomi/tachidesk/global/impl/WebView.kt index 897a1c45..e7ece31b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/global/impl/WebView.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/global/impl/WebView.kt @@ -43,7 +43,8 @@ object WebView : Websocket() { } } - @Serializable public sealed class TypeObject + @Serializable + sealed class TypeObject @Serializable @SerialName("loadUrl") @@ -62,7 +63,7 @@ object WebView : Websocket() { @Serializable @SerialName("event") - public data class JsEventMessage( + data class JsEventMessage( val eventType: String, val clickX: Float, val clickY: Float, @@ -94,7 +95,6 @@ object WebView : Websocket() { logger.info { "Resize browser" } } is JsEventMessage -> { - val type = event.eventType dr.event(event) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt index 11dcd557..7ad5748d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt @@ -155,6 +155,9 @@ class SettingsMutation { updateSetting(settings.updateMangas, serverConfig.updateMangas) // Authentication + updateSetting(settings.authMode, serverConfig.authMode) + updateSetting(settings.authUsername, serverConfig.authUsername) + updateSetting(settings.authPassword, serverConfig.authPassword) updateSetting(settings.basicAuthEnabled, serverConfig.basicAuthEnabled) updateSetting(settings.basicAuthUsername, serverConfig.basicAuthUsername) updateSetting(settings.basicAuthPassword, serverConfig.basicAuthPassword) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/AuthMode.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/AuthMode.kt new file mode 100644 index 00000000..2a6f9733 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/AuthMode.kt @@ -0,0 +1,13 @@ +package suwayomi.tachidesk.graphql.types + +enum class AuthMode { + NONE, + BASIC_AUTH, + SIMPLE_LOGIN, + // TODO: ACCOUNT for #623 + ; + + companion object { + fun from(channel: String): AuthMode = entries.find { it.name.lowercase() == channel.lowercase() } ?: NONE + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SettingsType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SettingsType.kt index 9182e124..d0002a90 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SettingsType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SettingsType.kt @@ -63,8 +63,17 @@ interface Settings : Node { val updateMangas: Boolean? // Authentication + val authMode: AuthMode? + val authUsername: String? + val authPassword: String? + + @GraphQLDeprecated("Removed - prefer authMode") val basicAuthEnabled: Boolean? + + @GraphQLDeprecated("Removed - prefer authUsername") val basicAuthUsername: String? + + @GraphQLDeprecated("Removed - prefer authPassword") val basicAuthPassword: String? // misc @@ -144,8 +153,14 @@ data class PartialSettingsType( override val globalUpdateInterval: Double?, override val updateMangas: Boolean?, // Authentication + override val authMode: AuthMode?, + override val authUsername: String?, + override val authPassword: String?, + @GraphQLDeprecated("Removed - prefer authMode") override val basicAuthEnabled: Boolean?, + @GraphQLDeprecated("Removed - prefer authUsername") override val basicAuthUsername: String?, + @GraphQLDeprecated("Removed - prefer authPassword") override val basicAuthPassword: String?, // misc override val debugLogsEnabled: Boolean?, @@ -219,8 +234,14 @@ class SettingsType( override val globalUpdateInterval: Double, override val updateMangas: Boolean, // Authentication + override val authMode: AuthMode, + override val authUsername: String, + override val authPassword: String, + @GraphQLDeprecated("Removed - prefer authMode") override val basicAuthEnabled: Boolean, + @GraphQLDeprecated("Removed - prefer authUsername") override val basicAuthUsername: String, + @GraphQLDeprecated("Removed - prefer authPassword") override val basicAuthPassword: String, // misc override val debugLogsEnabled: Boolean, @@ -289,6 +310,9 @@ class SettingsType( config.globalUpdateInterval.value, config.updateMangas.value, // Authentication + config.authMode.value, + config.authUsername.value, + config.authPassword.value, config.basicAuthEnabled.value, config.basicAuthUsername.value, config.basicAuthPassword.value, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt index 557d117b..47a1a0d4 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt @@ -419,9 +419,12 @@ object ProtoBackupExport : ProtoBackupBase() { globalUpdateInterval = serverConfig.globalUpdateInterval.value, updateMangas = serverConfig.updateMangas.value, // Authentication - basicAuthEnabled = serverConfig.basicAuthEnabled.value, - basicAuthUsername = serverConfig.basicAuthUsername.value, - basicAuthPassword = serverConfig.basicAuthPassword.value, + authMode = serverConfig.authMode.value, + authUsername = serverConfig.authUsername.value, + authPassword = serverConfig.authPassword.value, + basicAuthEnabled = false, + basicAuthUsername = null, + basicAuthPassword = null, // misc debugLogsEnabled = serverConfig.debugLogsEnabled.value, gqlDebugLogsEnabled = false, // deprecated diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupImport.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupImport.kt index d2daa4c4..2f45a759 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupImport.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupImport.kt @@ -32,6 +32,7 @@ import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update import suwayomi.tachidesk.global.impl.GlobalMeta import suwayomi.tachidesk.graphql.mutations.SettingsMutation +import suwayomi.tachidesk.graphql.types.AuthMode import suwayomi.tachidesk.graphql.types.toStatus import suwayomi.tachidesk.manga.impl.Category import suwayomi.tachidesk.manga.impl.Category.modifyCategoriesMetas @@ -57,6 +58,7 @@ import suwayomi.tachidesk.manga.model.dataclass.TrackRecordDataClass import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable import suwayomi.tachidesk.server.database.dbTransaction +import suwayomi.tachidesk.server.serverConfig import java.io.InputStream import java.util.Date import java.util.Timer @@ -525,7 +527,15 @@ object ProtoBackupImport : ProtoBackupBase() { return } - SettingsMutation().updateSettings(backupServerSettings) + SettingsMutation().updateSettings( + backupServerSettings.copy( + // legacy settings cannot overwrite new settings + basicAuthEnabled = + backupServerSettings.basicAuthEnabled.takeIf { + serverConfig.authMode.value == AuthMode.NONE + }, + ), + ) } private fun TrackRecordDataClass.forComparison() = this.copy(id = 0, mangaId = 0) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupServerSettings.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupServerSettings.kt index 760b6f78..64ba171b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupServerSettings.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupServerSettings.kt @@ -3,6 +3,7 @@ package suwayomi.tachidesk.manga.impl.backup.proto.models import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import org.jetbrains.exposed.sql.SortOrder +import suwayomi.tachidesk.graphql.types.AuthMode import suwayomi.tachidesk.graphql.types.Settings import suwayomi.tachidesk.graphql.types.WebUIChannel import suwayomi.tachidesk.graphql.types.WebUIFlavor @@ -45,9 +46,13 @@ data class BackupServerSettings( @ProtoNumber(27) override var globalUpdateInterval: Double, @ProtoNumber(28) override var updateMangas: Boolean, // Authentication - @ProtoNumber(29) override var basicAuthEnabled: Boolean, - @ProtoNumber(30) override var basicAuthUsername: String, - @ProtoNumber(31) override var basicAuthPassword: String, + @ProtoNumber(56) override var authMode: AuthMode, + @ProtoNumber(29) override var basicAuthEnabled: Boolean?, + @ProtoNumber(30) override var authUsername: String, + @ProtoNumber(31) override var authPassword: String, + // deprecated + @ProtoNumber(99991) override var basicAuthUsername: String?, + @ProtoNumber(99992) override var basicAuthPassword: String?, // misc @ProtoNumber(32) override var debugLogsEnabled: Boolean, @ProtoNumber(33) override var gqlDebugLogsEnabled: Boolean, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt index 64b451b7..9cff65b3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt @@ -11,6 +11,8 @@ import io.github.oshai.kotlinlogging.KotlinLogging import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.path import io.javalin.http.HandlerType +import io.javalin.http.HttpStatus +import io.javalin.http.RedirectResponse import io.javalin.http.UnauthorizedResponse import io.javalin.http.staticfiles.Location import kotlinx.coroutines.CoroutineScope @@ -22,14 +24,19 @@ import kotlinx.coroutines.runBlocking import org.eclipse.jetty.server.ServerConnector import suwayomi.tachidesk.global.GlobalAPI import suwayomi.tachidesk.graphql.GraphQL +import suwayomi.tachidesk.graphql.types.AuthMode import suwayomi.tachidesk.manga.MangaAPI import suwayomi.tachidesk.opds.OpdsAPI +import suwayomi.tachidesk.server.generated.BuildConfig import suwayomi.tachidesk.server.util.Browser import suwayomi.tachidesk.server.util.WebInterfaceManager import uy.kohesive.injekt.injectLazy import java.io.IOException +import java.net.URLEncoder +import java.nio.charset.StandardCharsets import java.util.concurrent.CompletableFuture import kotlin.concurrent.thread +import kotlin.time.Duration.Companion.days object JavalinSetup { private val logger = KotlinLogging.logger {} @@ -111,8 +118,49 @@ object JavalinSetup { } } + app.get("/login.html") { ctx -> + var page = + 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") + val httpCacheSeconds = 1.days.inWholeSeconds + ctx.header("cache-control", "max-age=$httpCacheSeconds") + ctx.result(page) + } + + app.post("/login.html") { ctx -> + val username = ctx.formParam("user") + val password = ctx.formParam("pass") + val isValid = + username == serverConfig.authUsername.value && + password == serverConfig.authPassword.value + + if (isValid) { + val redirect = ctx.queryParam("redirect") ?: "/" + // NOTE: We currently have no session handler attached. + // Thus, all sessions are stored in memory and not persisted. + // Furthermore, default session timeout appears to be 30m + ctx.header("Location", redirect) + ctx.sessionAttribute("logged-in", username) + throw RedirectResponse(HttpStatus.SEE_OTHER) + } + + var page = + 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.req().session.invalidate() + ctx.result(page) + } + app.beforeMatched { ctx -> - val isWebManifest = listOf("site.webmanifest", "manifest.json").any { ctx.path().endsWith(it) } + val isWebManifest = listOf("site.webmanifest", "manifest.json", "login.html").any { ctx.path().endsWith(it) } val isPreFlight = ctx.method() == HandlerType.OPTIONS val requiresAuthentication = !isPreFlight && !isWebManifest @@ -120,14 +168,31 @@ object JavalinSetup { return@beforeMatched } + val authMode = serverConfig.authMode.value ?: AuthMode.NONE + fun credentialsValid(): Boolean { val basicAuthCredentials = ctx.basicAuthCredentials() ?: return false val (username, password) = basicAuthCredentials - return username == serverConfig.basicAuthUsername.value && - password == serverConfig.basicAuthPassword.value + return username == serverConfig.authUsername.value && + password == serverConfig.authPassword.value } - if (serverConfig.basicAuthEnabled.value && !credentialsValid()) { + fun cookieValid(): Boolean { + val username = ctx.sessionAttribute("logged-in") ?: return false + return username == serverConfig.authUsername.value + } + + if (authMode == AuthMode.SIMPLE_LOGIN && !cookieValid() && ctx.path().startsWith("/api")) { + throw UnauthorizedResponse() + } + + if (authMode == AuthMode.SIMPLE_LOGIN && !cookieValid()) { + val url = "/login.html?redirect=" + URLEncoder.encode(ctx.fullUrl(), StandardCharsets.UTF_8) + ctx.header("Location", url) + throw RedirectResponse(HttpStatus.SEE_OTHER) + } + + if (authMode == AuthMode.BASIC_AUTH && !credentialsValid()) { ctx.header("WWW-Authenticate", "Basic") throw UnauthorizedResponse() } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt index cb2e778b..7b2ec743 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt @@ -24,6 +24,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach import org.jetbrains.exposed.sql.SortOrder +import suwayomi.tachidesk.graphql.types.AuthMode import suwayomi.tachidesk.graphql.types.WebUIChannel import suwayomi.tachidesk.graphql.types.WebUIFlavor import suwayomi.tachidesk.graphql.types.WebUIInterface @@ -89,6 +90,42 @@ class ServerConfig( .map { configAdapter.toType(it) } } + open inner class MigratedConfigValue( + private val readMigrated: () -> Any, + private val setMigrated: (T) -> Unit, + ) { + private var flow: MutableStateFlow? = null + + open fun getValueFromConfig( + thisRef: ServerConfig, + property: KProperty<*>, + ): Any = readMigrated() + + operator fun getValue( + thisRef: ServerConfig, + property: KProperty<*>, + ): MutableStateFlow { + if (flow != null) { + return flow!! + } + + @Suppress("UNCHECKED_CAST") + val value = getValueFromConfig(thisRef, property) as T + + val stateFlow = MutableStateFlow(value) + flow = stateFlow + + stateFlow + .drop(1) + .distinctUntilChanged() + .filter { it != getValueFromConfig(thisRef, property) } + .onEach(setMigrated) + .launchIn(mutableConfigValueScope) + + return stateFlow + } + } + val ip: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) val port: MutableStateFlow by OverrideConfigValue(IntConfigAdapter) @@ -120,14 +157,6 @@ class ServerConfig( // extensions val extensionRepos: MutableStateFlow> by OverrideConfigValues(StringConfigAdapter) - // playwright webview - val playwrightBrowser: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) - val playwrightWsEndpoint: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) - val playwrightSandbox: MutableStateFlow by OverrideConfigValue(BooleanConfigAdapter) - - // webview - val webviewImpl: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) - // requests val maxSourcesInParallel: MutableStateFlow by OverrideConfigValue(IntConfigAdapter) @@ -139,9 +168,20 @@ class ServerConfig( val updateMangas: MutableStateFlow by OverrideConfigValue(BooleanConfigAdapter) // Authentication - val basicAuthEnabled: MutableStateFlow by OverrideConfigValue(BooleanConfigAdapter) - val basicAuthUsername: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) - val basicAuthPassword: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) + val authMode: MutableStateFlow by OverrideConfigValue(EnumConfigAdapter(AuthMode::class.java)) + val authUsername: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) + val authPassword: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) + val basicAuthEnabled: MutableStateFlow by MigratedConfigValue({ + authMode.value == AuthMode.BASIC_AUTH + }) { + authMode.value = if (it) AuthMode.BASIC_AUTH else AuthMode.NONE + } + val basicAuthUsername: MutableStateFlow by MigratedConfigValue({ authUsername.value }) { + authUsername.value = it + } + val basicAuthPassword: MutableStateFlow by MigratedConfigValue({ authPassword.value }) { + authPassword.value = it + } // misc val debugLogsEnabled: MutableStateFlow by OverrideConfigValue(BooleanConfigAdapter) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt index 3dc6c6b4..dd63d287 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt @@ -9,7 +9,12 @@ package suwayomi.tachidesk.server import android.os.Looper import ch.qos.logback.classic.Level +import com.typesafe.config.Config +import com.typesafe.config.ConfigException import com.typesafe.config.ConfigRenderOptions +import com.typesafe.config.ConfigValue +import com.typesafe.config.ConfigValueFactory +import com.typesafe.config.parser.ConfigDocument import dev.datlag.kcef.KCEF import eu.kanade.tachiyomi.App import eu.kanade.tachiyomi.createAppModule @@ -32,12 +37,14 @@ import org.koin.core.context.startKoin import org.koin.core.module.Module import org.koin.dsl.module import suwayomi.tachidesk.global.impl.KcefWebView.Companion.toCefCookie +import suwayomi.tachidesk.graphql.types.AuthMode import suwayomi.tachidesk.i18n.LocalizationHelper import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupExport import suwayomi.tachidesk.manga.impl.download.DownloadManager import suwayomi.tachidesk.manga.impl.update.IUpdater import suwayomi.tachidesk.manga.impl.update.Updater import suwayomi.tachidesk.manga.impl.util.lang.renameTo +import suwayomi.tachidesk.server.BooleanConfigAdapter import suwayomi.tachidesk.server.database.databaseUp import suwayomi.tachidesk.server.generated.BuildConfig import suwayomi.tachidesk.server.util.AppMutex.handleAppMutex @@ -127,6 +134,29 @@ fun setupLogLevelUpdating( ) } +fun migrateConfig( + configDocument: ConfigDocument, + config: Config, + configKey: String, + toConfigKey: String, + toType: (ConfigValue) -> T?, +): ConfigDocument { + try { + val configValue = config.getValue(configKey) + val typedValue = toType(configValue) + if (typedValue != null) { + return configDocument.withValue( + toConfigKey, + ConfigValueFactory.fromAnyRef(typedValue), + ) + } + } catch (_: ConfigException) { + // ignore, likely already migrated + } + + return configDocument +} + fun serverModule(applicationDirs: ApplicationDirs): Module = module { single { applicationDirs } @@ -268,7 +298,40 @@ fun applicationSetup() { } } else { // make sure the user config file is up-to-date - GlobalConfigManager.updateUserConfig() + GlobalConfigManager.updateUserConfig { config -> + var updatedConfig = this + updatedConfig = + migrateConfig( + updatedConfig, + config, + "server.basicAuthEnabled", + "server.authMode", + toType = { + if (it.unwrapped() as? Boolean == true) { + AuthMode.BASIC_AUTH.name + } else { + null + } + }, + ) + updatedConfig = + migrateConfig( + updatedConfig, + config, + "server.basicAuthUsername", + "server.authUsername", + toType = { it.unwrapped() as? String }, + ) + updatedConfig = + migrateConfig( + updatedConfig, + config, + "server.basicAuthPassword", + "server.authPassword", + toType = { it.unwrapped() as? String }, + ) + updatedConfig + } } } catch (e: Exception) { logger.error(e) { "Exception while creating initial server.conf" } diff --git a/server/src/main/resources/server-reference.conf b/server/src/main/resources/server-reference.conf index b4b0d988..86c6a9ae 100644 --- a/server/src/main/resources/server-reference.conf +++ b/server/src/main/resources/server-reference.conf @@ -43,9 +43,9 @@ server.globalUpdateInterval = 12 # time in hours - 0 to disable it - (doesn't ha server.updateMangas = false # if the mangas should be updated along with the chapter list during a library/category update # Authentication -server.basicAuthEnabled = false -server.basicAuthUsername = "" -server.basicAuthPassword = "" +server.authMode = "none" # none, basic_auth or simple_login +server.authUsername = "" +server.authPassword = "" # misc server.debugLogsEnabled = false diff --git a/server/src/main/resources/static/login.html b/server/src/main/resources/static/login.html new file mode 100644 index 00000000..12ff5011 --- /dev/null +++ b/server/src/main/resources/static/login.html @@ -0,0 +1,164 @@ + + + + + Suwayomi Login + + + +
+

Suwayomi

+
+
+
[ERROR]
+
+

Login

+
+ + + + +
+
+ +
+
+
+ + + diff --git a/server/src/main/resources/webview.html b/server/src/main/resources/webview.html index e8299d18..c376de50 100644 --- a/server/src/main/resources/webview.html +++ b/server/src/main/resources/webview.html @@ -15,6 +15,9 @@ body { display: flex; flex-direction: column; + font-family: "Roboto","Helvetica","Arial",sans-serif; + font-weight: 400; + letter-spacing: 0em; } body.disconnected::after { content: 'Disconnected, please refresh'; @@ -30,7 +33,8 @@ cursor: not-allowed; } header { - background-color: rgb(12, 16, 33); + background-color: rgb(34, 38, 53); + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 4px -1px, rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px; color: #fff; padding: 8px 32px; } diff --git a/server/src/test/resources/server-reference.conf b/server/src/test/resources/server-reference.conf index 34886ce8..14ccfbe2 100644 --- a/server/src/test/resources/server-reference.conf +++ b/server/src/test/resources/server-reference.conf @@ -43,9 +43,9 @@ server.globalUpdateInterval = 12 # time in hours - 0 to disable it - (doesn't ha server.updateMangas = false # if the mangas should be updated along with the chapter list during a library/category update # Authentication -server.basicAuthEnabled = false -server.basicAuthUsername = "" -server.basicAuthPassword = "" +server.authMode = "none" # none, basic_auth or simple_login +server.authUsername = "" +server.authPassword = "" # misc server.debugLogsEnabled = false