Feature/make config settings changeable during runtime (#545)

* Add logic to update config during runtime

* Update ConfigModule to always use the latest config

* Make ServerConfig settings re-assignable
This commit is contained in:
schroda
2023-06-05 15:18:18 +02:00
committed by GitHub
parent a64566c0f3
commit 51bfdc0947
7 changed files with 82 additions and 59 deletions

View File

@@ -11,6 +11,9 @@ import ch.qos.logback.classic.Level
import com.typesafe.config.Config import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigRenderOptions
import com.typesafe.config.ConfigValue
import com.typesafe.config.ConfigValueFactory
import com.typesafe.config.parser.ConfigDocumentFactory
import mu.KotlinLogging import mu.KotlinLogging
import java.io.File import java.io.File
@@ -18,15 +21,17 @@ import java.io.File
* Manages app config. * Manages app config.
*/ */
open class ConfigManager { open class ConfigManager {
val logger = KotlinLogging.logger {}
private val generatedModules = mutableMapOf<Class<out ConfigModule>, ConfigModule>() private val generatedModules = mutableMapOf<Class<out ConfigModule>, ConfigModule>()
val config by lazy { loadConfigs() } private val userConfigFile = File(ApplicationRootDir, "server.conf")
private var internalConfig = loadConfigs()
val config: Config
get() = internalConfig
// Public read-only view of modules // Public read-only view of modules
val loadedModules: Map<Class<out ConfigModule>, ConfigModule> val loadedModules: Map<Class<out ConfigModule>, ConfigModule>
get() = generatedModules get() = generatedModules
val logger = KotlinLogging.logger {}
/** /**
* Get a config module * Get a config module
*/ */
@@ -54,7 +59,7 @@ open class ConfigManager {
// Load user config // Load user config
val userConfig = val userConfig =
File(ApplicationRootDir, "server.conf").let { userConfigFile.let {
ConfigFactory.parseFile(it) ConfigFactory.parseFile(it)
} }
@@ -86,6 +91,20 @@ open class ConfigManager {
registerModule(it) registerModule(it)
} }
} }
private fun updateUserConfigFile(path: String, value: ConfigValue) {
val userConfigDoc = ConfigDocumentFactory.parseFile(userConfigFile)
val updatedConfigDoc = userConfigDoc.withValue(path, value)
val newFileContent = updatedConfigDoc.render()
userConfigFile.writeText(newFileContent)
}
fun updateValue(path: String, value: Any) {
val configValue = ConfigValueFactory.fromAnyRef(value)
updateUserConfigFile(path, configValue)
internalConfig = internalConfig.withValue(path, configValue)
}
} }
object GlobalConfigManager : ConfigManager() object GlobalConfigManager : ConfigManager()

View File

@@ -15,19 +15,23 @@ import kotlin.reflect.KProperty
* Abstract config module. * Abstract config module.
*/ */
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
abstract class ConfigModule(config: Config) abstract class ConfigModule(getConfig: () -> Config)
/** /**
* Abstract jvm-commandline-argument-overridable config module. * Abstract jvm-commandline-argument-overridable config module.
*/ */
abstract class SystemPropertyOverridableConfigModule(config: Config, moduleName: String) : ConfigModule(config) { abstract class SystemPropertyOverridableConfigModule(getConfig: () -> Config, moduleName: String) : ConfigModule(getConfig) {
val overridableConfig = SystemPropertyOverrideDelegate(config, moduleName) val overridableConfig = SystemPropertyOverrideDelegate(getConfig, moduleName)
} }
/** Defines a config property that is overridable with jvm `-D` commandline arguments prefixed with [CONFIG_PREFIX] */ /** Defines a config property that is overridable with jvm `-D` commandline arguments prefixed with [CONFIG_PREFIX] */
class SystemPropertyOverrideDelegate(val config: Config, val moduleName: String) { class SystemPropertyOverrideDelegate(val getConfig: () -> Config, val moduleName: String) {
operator fun <R> setValue(thisRef: R, property: KProperty<*>, value: Any) {
GlobalConfigManager.updateValue("$moduleName.${property.name}", value)
}
inline operator fun <R, reified T> getValue(thisRef: R, property: KProperty<*>): T { inline operator fun <R, reified T> getValue(thisRef: R, property: KProperty<*>): T {
val configValue: T = config.getValue(thisRef, property) val configValue: T = getConfig().getValue(thisRef, property)
val combined = System.getProperty( val combined = System.getProperty(
"$CONFIG_PREFIX.$moduleName.${property.name}", "$CONFIG_PREFIX.$moduleName.${property.name}",

View File

@@ -8,12 +8,12 @@ import xyz.nulldev.ts.config.ConfigModule
* Application info config. * Application info config.
*/ */
class ApplicationInfoConfigModule(config: Config) : ConfigModule(config) { class ApplicationInfoConfigModule(getConfig: () -> Config) : ConfigModule(getConfig) {
val packageName: String by config val packageName: String by getConfig()
val debug: Boolean by config val debug: Boolean by getConfig()
companion object { companion object {
fun register(config: Config) = fun register(config: Config) =
ApplicationInfoConfigModule(config.getConfig("android.app")) ApplicationInfoConfigModule { config.getConfig("android.app") }
} }
} }

View File

@@ -8,27 +8,27 @@ import xyz.nulldev.ts.config.ConfigModule
* Files configuration modules. Specifies where to store the Android files. * Files configuration modules. Specifies where to store the Android files.
*/ */
class FilesConfigModule(config: Config) : ConfigModule(config) { class FilesConfigModule(getConfig: () -> Config) : ConfigModule(getConfig) {
val dataDir: String by config val dataDir: String by getConfig()
val filesDir: String by config val filesDir: String by getConfig()
val noBackupFilesDir: String by config val noBackupFilesDir: String by getConfig()
val externalFilesDirs: MutableList<String> by config val externalFilesDirs: MutableList<String> by getConfig()
val obbDirs: MutableList<String> by config val obbDirs: MutableList<String> by getConfig()
val cacheDir: String by config val cacheDir: String by getConfig()
val codeCacheDir: String by config val codeCacheDir: String by getConfig()
val externalCacheDirs: MutableList<String> by config val externalCacheDirs: MutableList<String> by getConfig()
val externalMediaDirs: MutableList<String> by config val externalMediaDirs: MutableList<String> by getConfig()
val rootDir: String by config val rootDir: String by getConfig()
val externalStorageDir: String by config val externalStorageDir: String by getConfig()
val downloadCacheDir: String by config val downloadCacheDir: String by getConfig()
val databasesDir: String by config val databasesDir: String by getConfig()
val prefsDir: String by config val prefsDir: String by getConfig()
val packageDir: String by config val packageDir: String by getConfig()
companion object { companion object {
fun register(config: Config) = fun register(config: Config) =
FilesConfigModule(config.getConfig("android.files")) FilesConfigModule { config.getConfig("android.files") }
} }
} }

View File

@@ -4,19 +4,19 @@ import com.typesafe.config.Config
import io.github.config4k.getValue import io.github.config4k.getValue
import xyz.nulldev.ts.config.ConfigModule import xyz.nulldev.ts.config.ConfigModule
class SystemConfigModule(val config: Config) : ConfigModule(config) { class SystemConfigModule(val getConfig: () -> Config) : ConfigModule(getConfig) {
val isDebuggable: Boolean by config val isDebuggable: Boolean by getConfig()
val propertyPrefix = "properties." val propertyPrefix = "properties."
fun getStringProperty(property: String) = config.getString("$propertyPrefix$property")!! fun getStringProperty(property: String) = getConfig().getString("$propertyPrefix$property")!!
fun getIntProperty(property: String) = config.getInt("$propertyPrefix$property") fun getIntProperty(property: String) = getConfig().getInt("$propertyPrefix$property")
fun getLongProperty(property: String) = config.getLong("$propertyPrefix$property") fun getLongProperty(property: String) = getConfig().getLong("$propertyPrefix$property")
fun getBooleanProperty(property: String) = config.getBoolean("$propertyPrefix$property") fun getBooleanProperty(property: String) = getConfig().getBoolean("$propertyPrefix$property")
fun hasProperty(property: String) = config.hasPath("$propertyPrefix$property") fun hasProperty(property: String) = getConfig().hasPath("$propertyPrefix$property")
companion object { companion object {
fun register(config: Config) = fun register(config: Config) =
SystemConfigModule(config.getConfig("android.system")) SystemConfigModule { config.getConfig("android.system") }
} }
} }

View File

@@ -13,39 +13,39 @@ import xyz.nulldev.ts.config.SystemPropertyOverridableConfigModule
import xyz.nulldev.ts.config.debugLogsEnabled import xyz.nulldev.ts.config.debugLogsEnabled
private const val MODULE_NAME = "server" private const val MODULE_NAME = "server"
class ServerConfig(config: Config, moduleName: String = MODULE_NAME) : SystemPropertyOverridableConfigModule(config, moduleName) { class ServerConfig(getConfig: () -> Config, moduleName: String = MODULE_NAME) : SystemPropertyOverridableConfigModule(getConfig, moduleName) {
val ip: String by overridableConfig var ip: String by overridableConfig
val port: Int by overridableConfig var port: Int by overridableConfig
// proxy // proxy
val socksProxyEnabled: Boolean by overridableConfig var socksProxyEnabled: Boolean by overridableConfig
val socksProxyHost: String by overridableConfig var socksProxyHost: String by overridableConfig
val socksProxyPort: String by overridableConfig var socksProxyPort: String by overridableConfig
// webUI // webUI
val webUIEnabled: Boolean by overridableConfig var webUIEnabled: Boolean by overridableConfig
val webUIFlavor: String by overridableConfig var webUIFlavor: String by overridableConfig
val initialOpenInBrowserEnabled: Boolean by overridableConfig var initialOpenInBrowserEnabled: Boolean by overridableConfig
val webUIInterface: String by overridableConfig var webUIInterface: String by overridableConfig
val electronPath: String by overridableConfig var electronPath: String by overridableConfig
// downloader // downloader
val downloadAsCbz: Boolean by overridableConfig var downloadAsCbz: Boolean by overridableConfig
val downloadsPath: String by overridableConfig var downloadsPath: String by overridableConfig
// updater // updater
val maxParallelUpdateRequests: Int by overridableConfig var maxParallelUpdateRequests: Int by overridableConfig
// Authentication // Authentication
val basicAuthEnabled: Boolean by overridableConfig var basicAuthEnabled: Boolean by overridableConfig
val basicAuthUsername: String by overridableConfig var basicAuthUsername: String by overridableConfig
val basicAuthPassword: String by overridableConfig var basicAuthPassword: String by overridableConfig
// misc // misc
val debugLogsEnabled: Boolean = debugLogsEnabled(GlobalConfigManager.config) var debugLogsEnabled: Boolean = debugLogsEnabled(GlobalConfigManager.config)
val systemTrayEnabled: Boolean by overridableConfig var systemTrayEnabled: Boolean by overridableConfig
companion object { companion object {
fun register(config: Config) = ServerConfig(config.getConfig(MODULE_NAME)) fun register(getConfig: () -> Config) = ServerConfig({ getConfig().getConfig(MODULE_NAME) })
} }
} }

View File

@@ -60,7 +60,7 @@ fun applicationSetup() {
// register Tachidesk's config which is dubbed "ServerConfig" // register Tachidesk's config which is dubbed "ServerConfig"
GlobalConfigManager.registerModule( GlobalConfigManager.registerModule(
ServerConfig.register(GlobalConfigManager.config) ServerConfig.register { GlobalConfigManager.config }
) )
// Application dirs // Application dirs