From 065beb72e1e383327dd6aa39aacbeb33857b0956 Mon Sep 17 00:00:00 2001 From: Nicolai Dagestad Date: Sun, 11 Aug 2024 20:30:08 +0200 Subject: [PATCH] First attempt to add https syncing --- .../kotatsu/settings/SyncSettingsFragment.kt | 6 ++--- .../utils/validation/DomainValidator.kt | 24 ++++++++++++------- .../koitharu/kotatsu/sync/data/SyncAuthApi.kt | 14 ++--------- .../kotatsu/sync/data/SyncAuthenticator.kt | 2 +- .../kotatsu/sync/data/SyncSettings.kt | 12 +++++----- .../kotatsu/sync/domain/SyncAuthResult.kt | 2 +- .../kotatsu/sync/domain/SyncHelper.kt | 13 +--------- .../kotatsu/sync/ui/SyncAuthActivity.kt | 8 +++---- .../kotatsu/sync/ui/SyncAuthViewModel.kt | 8 +++---- .../kotatsu/sync/ui/SyncHostDialogFragment.kt | 22 ++++++++++------- app/src/main/res/values/constants.xml | 6 ++--- app/src/main/res/values/strings.xml | 4 ++-- 12 files changed, 55 insertions(+), 66 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SyncSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SyncSettingsFragment.kt index 86ae24845..4902b8130 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SyncSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SyncSettingsFragment.kt @@ -29,7 +29,7 @@ class SyncSettingsFragment : BasePreferenceFragment(R.string.sync_settings), Fra override fun onPreferenceTreeClick(preference: Preference): Boolean { return when (preference.key) { - SyncSettings.KEY_HOST -> { + SyncSettings.KEY_SYNC_URL -> { SyncHostDialogFragment.show(childFragmentManager, null) true } @@ -43,7 +43,7 @@ class SyncSettingsFragment : BasePreferenceFragment(R.string.sync_settings), Fra } private fun bindHostSummary() { - val preference = findPreference(SyncSettings.KEY_HOST) ?: return - preference.summary = syncSettings.host + val preference = findPreference(SyncSettings.KEY_SYNC_URL) ?: return + preference.summary = syncSettings.syncURL } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/utils/validation/DomainValidator.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/utils/validation/DomainValidator.kt index c596a2321..507cf1850 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/utils/validation/DomainValidator.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/utils/validation/DomainValidator.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.settings.utils.validation +import android.webkit.URLUtil import okhttp3.HttpUrl import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.util.EditTextValidator @@ -20,15 +21,20 @@ class DomainValidator : EditTextValidator() { companion object { - fun isValidDomain(value: String): Boolean = runCatching { - require(value.isNotEmpty()) - val parts = value.split(':') - require(parts.size <= 2) - val urlBuilder = HttpUrl.Builder() - urlBuilder.host(parts.first()) - if (parts.size == 2) { - urlBuilder.port(parts[1].toInt()) + fun isValidDomain(value: String): Boolean { + if ( ! URLUtil.isValidUrl(value) ) { + return runCatching { + require(value.isNotEmpty()) + val parts = value.split(':') + require(parts.size <= 2) + val urlBuilder = HttpUrl.Builder() + urlBuilder.host(parts.first()) + if (parts.size == 2) { + urlBuilder.port(parts[1].toInt()) + } + }.isSuccess } - }.isSuccess + return true + } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt index b9f6b6683..0a27f1ca1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt @@ -17,13 +17,12 @@ class SyncAuthApi @Inject constructor( @BaseHttpClient private val okHttpClient: OkHttpClient, ) { - suspend fun authenticate(host: String, email: String, password: String): String { + suspend fun authenticate(syncURL: String, email: String, password: String): String { val body = JSONObject( mapOf("email" to email, "password" to password), ).toRequestBody() - val scheme = getScheme(host) val request = Request.Builder() - .url("$scheme://$host/auth") + .url("$syncURL/auth") .post(body) .build() val response = okHttpClient.newCall(request).await() @@ -35,13 +34,4 @@ class SyncAuthApi @Inject constructor( throw SyncApiException(message, code) } } - - private suspend fun getScheme(host: String): String { - val request = Request.Builder() - .url("http://$host/") - .head() - .build() - val response = okHttpClient.newCall(request).await() - return response.request.url.scheme - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthenticator.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthenticator.kt index dc660e381..b5840dfbd 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthenticator.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncAuthenticator.kt @@ -32,7 +32,7 @@ class SyncAuthenticator( private fun tryRefreshToken() = runCatching { runBlocking { authApi.authenticate( - syncSettings.host, + syncSettings.syncURL, account.name, accountManager.getPassword(account), ) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncSettings.kt index 636b96a69..dd9c7e856 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/data/SyncSettings.kt @@ -24,22 +24,22 @@ class SyncSettings( ) private val accountManager = AccountManager.get(context) - private val defaultHost = context.getString(R.string.sync_host_default) + private val defaultSyncUrl = context.getString(R.string.sync_url_default) @get:WorkerThread @set:WorkerThread - var host: String + var syncURL: String get() = account?.let { - accountManager.getUserData(it, KEY_HOST) - }.ifNullOrEmpty { defaultHost } + accountManager.getUserData(it, KEY_SYNC_URL) + }.ifNullOrEmpty { defaultSyncUrl } set(value) { account?.let { - accountManager.setUserData(it, KEY_HOST, value) + accountManager.setUserData(it, KEY_SYNC_URL, value) } } companion object { - const val KEY_HOST = "host" + const val KEY_SYNC_URL = "host" } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncAuthResult.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncAuthResult.kt index e8d29ec70..590ac2bc1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncAuthResult.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncAuthResult.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.sync.domain data class SyncAuthResult( - val host: String, + val syncURL: String, val email: String, val password: String, val token: String, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncHelper.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncHelper.kt index 666476c48..ae68969e0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncHelper.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/domain/SyncHelper.kt @@ -60,9 +60,7 @@ class SyncHelper @AssistedInject constructor( .addInterceptor(SyncInterceptor(context, account)) .build() private val baseUrl: String by lazy { - val host = settings.host - val scheme = getScheme(host) - "$scheme://$host" + settings.syncURL } private val defaultGcPeriod: Long // gc period if sync enabled get() = TimeUnit.DAYS.toMillis(4) @@ -273,15 +271,6 @@ class SyncHelper @AssistedInject constructor( return requireNotNull(tag) } - private fun getScheme(host: String): String { - val request = Request.Builder() - .url("http://$host/") - .head() - .build() - val response = httpClient.newCall(request).execute() - return response.request.url.scheme - } - private fun gcFavourites() { val deletedAt = System.currentTimeMillis() - defaultGcPeriod val selection = "deleted_at != 0 AND deleted_at < ?" diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt index 2f90ac328..c8e71f564 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt @@ -104,14 +104,14 @@ class SyncAuthActivity : BaseActivity(), View.OnClickLi } R.id.button_settings -> { - SyncHostDialogFragment.show(supportFragmentManager, viewModel.host.value) + SyncHostDialogFragment.show(supportFragmentManager, viewModel.syncURL.value) } } } override fun onFragmentResult(requestKey: String, result: Bundle) { - val host = result.getString(SyncHostDialogFragment.KEY_HOST) ?: return - viewModel.host.value = host + val syncURL = result.getString(SyncHostDialogFragment.KEY_SYNC_URL) ?: return + viewModel.syncURL.value = syncURL } override fun finish() { @@ -144,7 +144,7 @@ class SyncAuthActivity : BaseActivity(), View.OnClickLi val am = AccountManager.get(this) val account = Account(authResult.email, getString(R.string.account_type_sync)) val userdata = Bundle(1) - userdata.putString(SyncSettings.KEY_HOST, authResult.host) + userdata.putString(SyncSettings.KEY_SYNC_URL, authResult.syncURL) val result = Bundle() if (am.addAccountExplicitly(account, authResult.password, userdata)) { result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt index be890d55c..33d062b57 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt @@ -22,7 +22,7 @@ class SyncAuthViewModel @Inject constructor( val onAccountAlreadyExists = MutableEventFlow() val onTokenObtained = MutableEventFlow() - val host = MutableStateFlow(context.getString(R.string.sync_host_default)) + val syncURL = MutableStateFlow(context.getString(R.string.sync_url_default)) init { launchJob(Dispatchers.Default) { @@ -35,10 +35,10 @@ class SyncAuthViewModel @Inject constructor( } fun obtainToken(email: String, password: String) { - val hostValue = host.value + val urlValue = syncURL.value launchLoadingJob(Dispatchers.Default) { - val token = api.authenticate(hostValue, email, password) - val result = SyncAuthResult(host.value, email, password, token) + val token = api.authenticate(urlValue, email, password) + val result = SyncAuthResult(syncURL.value, email, password, token) onTokenObtained.call(result) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncHostDialogFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncHostDialogFragment.kt index 6c22040a7..9fe2052cc 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncHostDialogFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/sync/ui/SyncHostDialogFragment.kt @@ -49,10 +49,10 @@ class SyncHostDialogFragment : AlertDialogFragment { val result = requireViewBinding().edit.text?.toString().orEmpty() - syncSettings.host = result - parentFragmentManager.setFragmentResult(REQUEST_KEY, bundleOf(KEY_HOST to result)) + var scheme = "" + if ( ! result.startsWith("https://") && ! result.startsWith("http://")) { + scheme = "http://" + } + syncSettings.syncURL = "$scheme$result" + parentFragmentManager.setFragmentResult(REQUEST_KEY, bundleOf(KEY_SYNC_URL to "$scheme$result")) } } dialog.dismiss() @@ -75,11 +79,11 @@ class SyncHostDialogFragment : AlertDialogFragmenthttps://hosted.weblate.org/engage/kotatsu https://bugs.kotatsu.app/report org.kotatsu.sync - sync.kotatsu.app + https://sync.kotatsu.app Mw6F0tPEOgyV7F9U9Twg50Q8SndMY7hzIOfXg0AX_XU euBMt1GGRSDpVIFQVPxZrO7Kh6X4gWyv0dABuj4B-M8 9887 @@ -40,8 +40,8 @@ 0 1 - - @string/sync_host_default + + @string/sync_url_default moe.shirizu.org 86.57.183.214:8081 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 25edfd29e..b2c0caa41 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -278,7 +278,7 @@ Manga marked as NSFW will never be added to the history and your progress will not be saved Can help in case of some issues. All authorizations will be invalidated Show all - Invalid domain + Invalid server address Select range Clear all history Last 2 hours @@ -375,7 +375,7 @@ Find similar Synchronization settings Server address - You can use a self-hosted synchronization server or a default one. Don\'t change this if you\'re not sure what you\'re doing. + You can use a self-hosted synchronization server or a default one. Don\'t change this if you\'re not sure what you\'re doing. Ignore SSL errors Choose mirror automatically Automatically switch domains for manga sources on errors if mirrors are available