diff --git a/.gitignore b/.gitignore index 7e3ffdf..5473609 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ gradle.properties build server/src/main/resources/react +server/tmp/ +server/tachiserver-data/ \ No newline at end of file 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 59a43c1..c22d7d4 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 @@ -18,8 +18,6 @@ import java.io.File * Manages app config. */ open class ConfigManager { - private val dataRoot by lazy { AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)!! } - private val generatedModules = mutableMapOf, ConfigModule>() val config by lazy { loadConfigs() } @@ -27,8 +25,6 @@ open class ConfigManager { val loadedModules: Map, ConfigModule> get() = generatedModules - open val appConfigFile: String = "$dataRoot/server.conf" - val logger = KotlinLogging.logger {} /** @@ -51,8 +47,8 @@ open class ConfigManager { //Load user config val userConfig = - File(appConfigFile).let{ - ConfigFactory.parseFile(it) + File(System.getProperty("ir.armor.tachidesk.rootDir"), "server.conf").let { + ConfigFactory.parseFile(it) } val config = ConfigFactory.empty() @@ -69,7 +65,7 @@ open class ConfigManager { } fun registerModule(module: ConfigModule) { - generatedModules.put(module.javaClass, module) + generatedModules[module.javaClass] = module } fun registerModules(vararg modules: ConfigModule) { diff --git a/build.gradle.kts b/build.gradle.kts index a8dcfe6..3a1bc60 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -53,6 +53,7 @@ configure(projects) { val coroutinesVersion = "1.4.2" implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutinesVersion") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion") // Dependency Injection diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 3048893..6b2dfb7 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -80,6 +80,9 @@ dependencies { // uncomment to test extensions directly // implementation(fileTree("lib/")) + + // Testing + testImplementation(kotlin("test-junit5")) } val name = "ir.armor.tachidesk.Main" @@ -137,6 +140,9 @@ tasks { ) } } + test { + useJUnit() + } } launch4j { //used for windows diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/Extension.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/Extension.kt index f798857..3adef0b 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/Extension.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/Extension.kt @@ -39,12 +39,17 @@ import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update +import org.kodein.di.DI +import org.kodein.di.conf.global +import org.kodein.di.instance import uy.kohesive.injekt.injectLazy import java.io.File import java.io.InputStream object Extension { private val logger = KotlinLogging.logger {} + private val applicationDirs by DI.global.instance() + data class InstallableAPK( val apkFilePath: String, @@ -58,7 +63,7 @@ object Extension { return installAPK { val apkURL = ExtensionGithubApi.getApkUrl(extensionRecord) val apkName = Uri.parse(apkURL).lastPathSegment!! - val apkSavePath = "${ApplicationDirs.extensionsRoot}/$apkName" + val apkSavePath = "${applicationDirs.extensionsRoot}/$apkName" // download apk file downloadAPKFile(apkURL, apkSavePath) @@ -79,7 +84,7 @@ object Extension { if (!isInstalled) { val fileNameWithoutType = apkName.substringBefore(".apk") - val dirPathWithoutType = "${ApplicationDirs.extensionsRoot}/$fileNameWithoutType" + val dirPathWithoutType = "${applicationDirs.extensionsRoot}/$fileNameWithoutType" val jarFilePath = "$dirPathWithoutType.jar" val dexFilePath = "$dirPathWithoutType.dex" @@ -193,7 +198,7 @@ object Extension { val extensionRecord = transaction { ExtensionTable.select { ExtensionTable.pkgName eq pkgName }.firstOrNull()!! } val fileNameWithoutType = extensionRecord[ExtensionTable.apkName].substringBefore(".apk") - val jarPath = "${ApplicationDirs.extensionsRoot}/$fileNameWithoutType.jar" + val jarPath = "${applicationDirs.extensionsRoot}/$fileNameWithoutType.jar" transaction { val extensionId = extensionRecord[ExtensionTable.id].value @@ -232,7 +237,7 @@ object Extension { suspend fun getExtensionIcon(apkName: String): Pair { val iconUrl = transaction { ExtensionTable.select { ExtensionTable.apkName eq apkName }.firstOrNull()!! }[ExtensionTable.iconUrl] - val saveDir = "${ApplicationDirs.extensionsRoot}/icon" + val saveDir = "${applicationDirs.extensionsRoot}/icon" return getCachedImageResponse(saveDir, apkName) { network.client.newCall( diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/Manga.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/Manga.kt index 25b696b..4e3071b 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/Manga.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/Manga.kt @@ -22,6 +22,9 @@ import ir.armor.tachidesk.server.ApplicationDirs import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update +import org.kodein.di.DI +import org.kodein.di.conf.global +import org.kodein.di.instance import java.io.InputStream object Manga { @@ -95,9 +98,10 @@ object Manga { } } + private val applicationDirs by DI.global.instance() suspend fun getMangaThumbnail(mangaId: Int): Pair { val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! } - val saveDir = ApplicationDirs.thumbnailsRoot + val saveDir = applicationDirs.thumbnailsRoot val fileName = mangaId.toString() return getCachedImageResponse(saveDir, fileName) { diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/Page.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/Page.kt index aff86da..299ad8f 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/Page.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/Page.kt @@ -21,6 +21,9 @@ import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update +import org.kodein.di.DI +import org.kodein.di.conf.global +import org.kodein.di.instance import java.io.File import java.io.InputStream @@ -73,6 +76,7 @@ object Page { } // TODO: rewrite this to match tachiyomi + private val applicationDirs by DI.global.instance() fun getChapterDir(mangaId: Int, chapterId: Int): String { val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! } val sourceId = mangaEntry[MangaTable.sourceReference] @@ -88,7 +92,7 @@ object Page { val mangaTitle = mangaEntry[MangaTable.title] val sourceName = source.toString() - val mangaDir = "${ApplicationDirs.mangaRoot}/$sourceName/$mangaTitle/$chapterDir" + val mangaDir = "${applicationDirs.mangaRoot}/$sourceName/$mangaTitle/$chapterDir" // make sure dirs exist File(mangaDir).mkdirs() return mangaDir diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/util/GetHttpSource.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/util/GetHttpSource.kt index 41eb4ec..0c01ea9 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/util/GetHttpSource.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/util/GetHttpSource.kt @@ -16,10 +16,14 @@ import ir.armor.tachidesk.model.database.SourceTable import ir.armor.tachidesk.server.ApplicationDirs import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction +import org.kodein.di.DI +import org.kodein.di.conf.global +import org.kodein.di.instance import java.util.concurrent.ConcurrentHashMap object GetHttpSource { private val sourceCache = ConcurrentHashMap() + private val applicationDirs by DI.global.instance() fun getHttpSource(sourceId: Long): HttpSource { val cachedResult: HttpSource? = sourceCache[sourceId] @@ -39,7 +43,7 @@ object GetHttpSource { val apkName = extensionRecord[ExtensionTable.apkName] val className = extensionRecord[ExtensionTable.classFQName] val jarName = apkName.substringBefore(".apk") + ".jar" - val jarPath = "${ApplicationDirs.extensionsRoot}/$jarName" + val jarPath = "${applicationDirs.extensionsRoot}/$jarName" when (val instance = loadExtensionSources(jarPath, className)) { is Source -> listOf(instance) diff --git a/server/src/main/kotlin/ir/armor/tachidesk/impl/util/PackageTools.kt b/server/src/main/kotlin/ir/armor/tachidesk/impl/util/PackageTools.kt index a2177dc..44ea565 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/impl/util/PackageTools.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/impl/util/PackageTools.kt @@ -11,6 +11,9 @@ import ir.armor.tachidesk.server.ApplicationDirs import mu.KotlinLogging import net.dongliu.apk.parser.ApkFile import net.dongliu.apk.parser.ApkParsers +import org.kodein.di.DI +import org.kodein.di.conf.global +import org.kodein.di.instance import org.w3c.dom.Element import org.w3c.dom.Node import xyz.nulldev.androidcompat.pm.InstalledPackage.Companion.toList @@ -31,6 +34,7 @@ import javax.xml.parsers.DocumentBuilderFactory object PackageTools { private val logger = KotlinLogging.logger {} + private val applicationDirs by DI.global.instance() const val EXTENSION_FEATURE = "tachiyomi.extension" const val METADATA_SOURCE_CLASS = "tachiyomi.extension.class" @@ -65,7 +69,7 @@ object PackageTools { .skipExceptions(false) .to(jarFilePath) if (handler.hasException()) { - val errorFile: Path = File(ApplicationDirs.extensionsRoot).toPath().resolve("$fileNameWithoutType-error.txt") + val errorFile: Path = File(applicationDirs.extensionsRoot).toPath().resolve("$fileNameWithoutType-error.txt") logger.error( "Detail Error Information in File $errorFile\n" + "Please report this file to one of following link if possible (any one).\n" + diff --git a/server/src/main/kotlin/ir/armor/tachidesk/model/dataclass/DBMangaer.kt b/server/src/main/kotlin/ir/armor/tachidesk/model/dataclass/DBMangaer.kt index ec971dc..2d5c1f7 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/model/dataclass/DBMangaer.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/model/dataclass/DBMangaer.kt @@ -18,10 +18,14 @@ import ir.armor.tachidesk.server.ApplicationDirs import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.transactions.transaction +import org.kodein.di.DI +import org.kodein.di.conf.global +import org.kodein.di.instance object DBMangaer { val db by lazy { - Database.connect("jdbc:h2:${ApplicationDirs.dataRoot}/database", "org.h2.Driver") + val applicationDirs by DI.global.instance() + Database.connect("jdbc:h2:${applicationDirs.dataRoot}/database", "org.h2.Driver") } } diff --git a/server/src/main/kotlin/ir/armor/tachidesk/server/ServerSetup.kt b/server/src/main/kotlin/ir/armor/tachidesk/server/ServerSetup.kt index f8b0350..516b541 100644 --- a/server/src/main/kotlin/ir/armor/tachidesk/server/ServerSetup.kt +++ b/server/src/main/kotlin/ir/armor/tachidesk/server/ServerSetup.kt @@ -8,6 +8,7 @@ package ir.armor.tachidesk.server * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import ch.qos.logback.classic.Level +import com.typesafe.config.Config import eu.kanade.tachiyomi.App import ir.armor.tachidesk.Main import ir.armor.tachidesk.model.dataclass.makeDataBaseTables @@ -15,7 +16,11 @@ import ir.armor.tachidesk.server.util.systemTray import mu.KotlinLogging import net.harawata.appdirs.AppDirsFactory import org.kodein.di.DI +import org.kodein.di.bind import org.kodein.di.conf.global +import org.kodein.di.instance +import org.kodein.di.singleton +import org.slf4j.Logger import xyz.nulldev.androidcompat.AndroidCompat import xyz.nulldev.androidcompat.AndroidCompatInitializer import xyz.nulldev.ts.config.ConfigKodeinModule @@ -24,8 +29,9 @@ import java.io.File private val logger = KotlinLogging.logger {} -object ApplicationDirs { - val dataRoot = AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)!! +class ApplicationDirs( + val dataRoot: String = AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null) +) { val extensionsRoot = "$dataRoot/extensions" val thumbnailsRoot = "$dataRoot/thumbnails" val mangaRoot = "$dataRoot/manga" @@ -38,29 +44,48 @@ val systemTray by lazy { systemTray() } val androidCompat by lazy { AndroidCompat() } fun applicationSetup() { - // register server config - GlobalConfigManager.registerModule( - ServerConfig.register(GlobalConfigManager.config) - ) - - // set application wide logging level - if (serverConfig.debugLogsEnabled) { - (mu.KotlinLogging.logger(org.slf4j.Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger).level = Level.DEBUG + val rootDir: String? = System.getProperty("ir.armor.tachidesk.rootDir") + val dirs = if (rootDir != null) { + ApplicationDirs(rootDir) + } else { + ApplicationDirs().also { + System.setProperty("ir.armor.tachidesk.rootDir", it.dataRoot) + } } // make dirs we need listOf( - ApplicationDirs.dataRoot, - ApplicationDirs.extensionsRoot, - "${ApplicationDirs.extensionsRoot}/icon", - ApplicationDirs.thumbnailsRoot + dirs.dataRoot, + dirs.extensionsRoot, + dirs.extensionsRoot + "/icon", + dirs.thumbnailsRoot ).forEach { File(it).mkdirs() } + GlobalConfigManager.registerModule( + ServerConfig.register(GlobalConfigManager.config) + ) + + // Application dirs + DI.global.addImport(DI.Module("Server") { + bind() with singleton { dirs } + }) + // Load config API + DI.global.addImport(ConfigKodeinModule().create()) + // Load Android compatibility dependencies + AndroidCompatInitializer().init() + // start app + androidCompat.startApp(App()) + + // set application wide logging level + if (serverConfig.debugLogsEnabled) { + (KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger).level = Level.DEBUG + } + // create conf file if doesn't exist try { - val dataConfFile = File("${ApplicationDirs.dataRoot}/server.conf") + val dataConfFile = File("${dirs.dataRoot}/server.conf") if (!dataConfFile.exists()) { Main::class.java.getResourceAsStream("/server-reference.conf").use { input -> dataConfFile.outputStream().use { output -> @@ -75,19 +100,13 @@ fun applicationSetup() { makeDataBaseTables() // create system tray - if (serverConfig.systemTrayEnabled) + if (serverConfig.systemTrayEnabled) { try { systemTray } catch (e: Exception) { e.printStackTrace() } - - // Load config API - DI.global.addImport(ConfigKodeinModule().create()) - // Load Android compatibility dependencies - AndroidCompatInitializer().init() - // start app - androidCompat.startApp(App()) + } // Disable jetty's logging System.setProperty("org.eclipse.jetty.util.log.announce", "false") diff --git a/server/src/test/kotlin/ir/armor/tachidesk/TestExtensions.kt b/server/src/test/kotlin/ir/armor/tachidesk/TestExtensions.kt new file mode 100644 index 0000000..500309e --- /dev/null +++ b/server/src/test/kotlin/ir/armor/tachidesk/TestExtensions.kt @@ -0,0 +1,187 @@ +package ir.armor.tachidesk + +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.HttpSource +import ir.armor.tachidesk.impl.Extension.installExtension +import ir.armor.tachidesk.impl.Extension.uninstallExtension +import ir.armor.tachidesk.impl.Extension.updateExtension +import ir.armor.tachidesk.impl.ExtensionsList.getExtensionList +import ir.armor.tachidesk.impl.Source.getSourceList +import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource +import ir.armor.tachidesk.impl.util.awaitSingle +import ir.armor.tachidesk.model.dataclass.ExtensionDataClass +import ir.armor.tachidesk.server.applicationSetup +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Semaphore +import kotlinx.coroutines.sync.withPermit +import mu.KotlinLogging +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import rx.Observable +import java.io.File + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class TestExtensions { + private val logger = KotlinLogging.logger {} + private lateinit var extensions: List + private lateinit var sources: List + + private val mangaToFetch = mutableListOf>() + private val failedToFetch = mutableListOf>() + private val mangaFailedToFetch = mutableListOf>() + private val chaptersToFetch = mutableListOf>() + private val chaptersFailedToFetch = mutableListOf>() + private val chaptersPageListFailedToFetch = mutableListOf, Exception>>() + + @BeforeAll + fun setup() { + val dataRoot = File("tmp/TestDesk").absolutePath + System.setProperty("ir.armor.tachidesk.rootDir", dataRoot) + applicationSetup() + setLoggingEnabled(false) + runBlocking { + extensions = getExtensionList() + extensions.forEach { + when { + it.obsolete -> { + uninstallExtension(it.pkgName) + } + it.hasUpdate -> { + updateExtension(it.pkgName) + } + else -> { + installExtension(it.pkgName) + } + } + } + sources = getSourceList().map { getHttpSource(it.id.toLong()) } + } + setLoggingEnabled(true) + File("tmp/TestDesk/sources.txt").writeText(sources.joinToString("\n") { "${it.name} - ${it.lang.toUpperCase()} - ${it.id}" }) + } + + @Test + fun runTest() { + runBlocking(Dispatchers.Default) { + val semaphore = Semaphore(10) + sources.mapIndexed { index, source -> + async { + semaphore.withPermit { + logger.info { "$index - Now fetching popular manga from $source" } + try { + mangaToFetch += source to (source.fetchPopularManga(1) + .awaitSingleRepeat().mangas.firstOrNull() + ?: throw Exception("Source returned no manga")) + } catch (e: Exception) { + logger.warn { "Failed to fetch popular manga from $source: ${e.message}" } + failedToFetch += source to e + } + } + } + }.awaitAll() + File("tmp/TestDesk/failedToFetch.txt").writeText( + failedToFetch.joinToString("\n") { (source, exception) -> + "${source.name} (${source.lang.toUpperCase()}, ${source.id}):" + + " ${exception.message}" + } + ) + logger.info { "Now fetching manga info from ${mangaToFetch.size} sources" } + + mangaToFetch.mapIndexed { index, (source, manga) -> + async { + semaphore.withPermit { + logger.info { "$index - Now fetching manga from $source" } + try { + manga.copyFrom(source.fetchMangaDetails(manga).awaitSingleRepeat()) + manga.initialized = true + } catch (e: Exception) { + logger.warn { + "Failed to fetch manga info from $source for ${manga.title} (${source.mangaDetailsRequest(manga).url}): ${e.message}" + } + mangaFailedToFetch += Triple(source, manga, e) + } + } + } + }.awaitAll() + File("tmp/TestDesk/MangaFailedToFetch.txt").writeText( + mangaFailedToFetch.joinToString("\n") { (source, manga, exception) -> + "${source.name} (${source.lang}, ${source.id}):" + + " ${manga.title} (${source.mangaDetailsRequest(manga).url}):" + + " ${exception.message}" + } + ) + logger.info { "Now fetching manga chapters from ${mangaToFetch.size} sources" } + + mangaToFetch.filter { it.second.initialized }.mapIndexed { index, (source, manga) -> + async { + semaphore.withPermit { + logger.info { "$index - Now fetching manga chapters from $source" } + try { + chaptersToFetch += Triple( + source, + manga, + source.fetchChapterList(manga).awaitSingleRepeat().firstOrNull() ?: throw Exception("Source returned no chapters") + ) + } catch (e: Exception) { + logger.warn { + "Failed to fetch manga chapters from $source for ${manga.title} (${source.mangaDetailsRequest(manga).url}): ${e.message}" + } + chaptersFailedToFetch += Triple(source, manga, e) + } catch (e: NoClassDefFoundError) { + logger.warn { + "Failed to fetch manga chapters from $source for ${manga.title} (${source.mangaDetailsRequest(manga).url}): ${e.message}" + } + chaptersFailedToFetch += Triple(source, manga, e) + } + } + } + }.awaitAll() + + File("tmp/TestDesk/ChaptersFailedToFetch.txt").writeText( + chaptersFailedToFetch.joinToString("\n") { (source, manga, exception) -> + "${source.name} (${source.lang}, ${source.id}):" + + " ${manga.title} (${source.mangaDetailsRequest(manga).url}):" + + " ${exception.message}" + } + ) + + chaptersToFetch.mapIndexed { index, (source, manga, chapter) -> + async { + semaphore.withPermit { + logger.info { "$index - Now fetching page list from $source" } + try { + source.fetchPageList(chapter).awaitSingleRepeat() + } catch (e: Exception) { + logger.warn { + "Failed to fetch manga info from $source for ${manga.title} (${source.mangaDetailsRequest(manga).url}): ${e.message}" + } + chaptersPageListFailedToFetch += Triple(source, manga to chapter, e) + } + } + } + }.awaitAll() + + File("tmp/TestDesk/ChapterPageListFailedToFetch.txt").writeText( + chaptersPageListFailedToFetch.joinToString("\n") { (source, manga, exception) -> + "${source.name} (${source.lang}, ${source.id}):" + + " ${manga.first.title} (${source.mangaDetailsRequest(manga.first).url}):" + + " ${manga.second.name} (${manga.second.url}): ${exception.message}" + } + ) + } + } + + private suspend fun Observable.awaitSingleRepeat(): T { + for (i in 1..2) { + try { + return awaitSingle() + } catch (e: Exception) {} + } + return awaitSingle() + } +} \ No newline at end of file diff --git a/server/src/test/kotlin/ir/armor/tachidesk/TestUtils.kt b/server/src/test/kotlin/ir/armor/tachidesk/TestUtils.kt new file mode 100644 index 0000000..685a6ff --- /dev/null +++ b/server/src/test/kotlin/ir/armor/tachidesk/TestUtils.kt @@ -0,0 +1,12 @@ +package ir.armor.tachidesk + +import ch.qos.logback.classic.Level +import mu.KotlinLogging +import org.slf4j.Logger + +fun setLoggingEnabled(enabled: Boolean = true) { + val logger = (KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger) + logger.level = if (enabled) { + Level.DEBUG + } else Level.ERROR +} \ No newline at end of file diff --git a/server/src/test/resources/server-reference.conf b/server/src/test/resources/server-reference.conf new file mode 100644 index 0000000..9e49ccd --- /dev/null +++ b/server/src/test/resources/server-reference.conf @@ -0,0 +1,13 @@ +# Server ip and port bindings +server.ip = "0.0.0.0" +server.port = 4567 + +# Socks5 proxy +server.socksProxy = false +server.socksProxyHost = "" +server.socksProxyPort = "" + +# misc +server.debugLogsEnabled = true +server.systemTrayEnabled = false +server.initialOpenInBrowserEnabled = true