Standalone Shonar Desktop: vendor portable sources + local engine; decouple from ~/Projects/Shonar
- shared/ = portable Android-origin sources vendored from deferred/desktop-server (app/build.gradle.kts srcDir repointed; PlaybackController.kt excluded as Android-only) - backend/ = bundled-lite engine (SQLite + inline queue); .venv symlinked from the old checkout, PYTHONPATH pins THIS backend's code over any editable install - repoRoot() resolves this project dir (env SHONAR_REPO still wins); desktop-dev.sh watches shared/ + backend/ - Verified: :app:compileKotlin + :app:test green (23 tests); engine boots on :8010, self-migrates, /healthz ok
This commit is contained in:
commit
76c867fca4
136 changed files with 21099 additions and 0 deletions
96
shared/com/shonar/provider/ProviderRegistry.kt
Normal file
96
shared/com/shonar/provider/ProviderRegistry.kt
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package com.shonar.provider
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Maps provider ids to implementations. The active provider is whichever the
|
||||
* user selected during setup; switching is just changing this id — the app
|
||||
* never branches on concrete provider classes.
|
||||
*/
|
||||
class ProviderRegistry(
|
||||
private val factories: Map<String, () -> ShonarProvider>,
|
||||
/** Builds every provider HTTP client (TOFU trust + redacting logger). */
|
||||
val tls: TlsPolicy = defaultTls(),
|
||||
) {
|
||||
|
||||
private val _activeId = MutableStateFlow(LocalOnlyProvider.ID)
|
||||
val activeId: StateFlow<String> = _activeId
|
||||
|
||||
/** Nextcloud is the product default once its factory registers (P4). */
|
||||
val defaultProviderId: String
|
||||
get() = factories.keys.sorted().let { ids ->
|
||||
ids.firstOrNull { it == NEXTCLOUD_ID } ?: ids.firstOrNull() ?: LocalOnlyProvider.ID
|
||||
}
|
||||
|
||||
fun provider(id: String): ShonarProvider =
|
||||
factories[id]?.invoke() ?: throw ProviderError.InvalidUrl("Unknown provider: $id")
|
||||
|
||||
val active: ShonarProvider get() = provider(_activeId.value)
|
||||
|
||||
/** Selecting a provider does not touch existing local data. */
|
||||
fun select(id: String): ShonarProvider {
|
||||
if (id !in factories) throw ProviderError.InvalidUrl("Unknown provider: $id")
|
||||
_activeId.value = id
|
||||
return provider(id)
|
||||
}
|
||||
|
||||
fun available(): List<ProviderDescriptor> =
|
||||
factories.keys.map { provider(it).descriptor }
|
||||
|
||||
companion object {
|
||||
const val NEXTCLOUD_ID = "nextcloud"
|
||||
const val CUSTOM_SHONAR_ID = "custom-shonar"
|
||||
const val SYNC_FOLDER_ID = FolderSyncProvider.ID
|
||||
|
||||
/** Behaviour-identical default: system trust, logging off, no pins. */
|
||||
fun defaultTls(sink: (String) -> Unit = {}): TlsPolicy = TlsPolicy(
|
||||
tofu = TofuManager(TofuStore(com.shonar.settings.InMemorySettingsStore())),
|
||||
sink = sink,
|
||||
)
|
||||
|
||||
/**
|
||||
* Production factory map. P1 registered local-only, P3 the custom
|
||||
* SHONAR server, P4 Nextcloud + the sync folder. Network providers
|
||||
* are single shared instances (their sessions live in the secure
|
||||
* store, not in the object) so connect/reconnect state survives
|
||||
* `provider(id)` calls. P5: every HTTP client comes from [tlsPolicy].
|
||||
*/
|
||||
fun withDefaults(
|
||||
appFilesDir: java.io.File,
|
||||
secureStore: com.shonar.settings.SettingsStore =
|
||||
com.shonar.settings.InMemorySettingsStore(),
|
||||
plainStore: com.shonar.settings.SettingsStore =
|
||||
com.shonar.settings.InMemorySettingsStore(),
|
||||
tlsPolicy: TlsPolicy = defaultTls(),
|
||||
): ProviderRegistry {
|
||||
val custom = CustomShonarProvider(
|
||||
auth = ShonarAuthStore(secureStore),
|
||||
sidecarRoot = java.io.File(appFilesDir, "shonar-sidecars"),
|
||||
client = tlsPolicy.apiClient(),
|
||||
handshake = ShonarHandshake(tlsPolicy.probeClient(), tlsPolicy.tofu),
|
||||
)
|
||||
val nextcloud = NextcloudProvider(
|
||||
auth = NextcloudAuthStore(secureStore),
|
||||
client = tlsPolicy.nextcloudClient(),
|
||||
loginFlow = NextcloudAuth(tlsPolicy.nextcloudClient(), tlsPolicy.tofu),
|
||||
)
|
||||
val folder = FolderSyncProvider(pathStore = plainStore)
|
||||
return ProviderRegistry(
|
||||
mapOf(
|
||||
LocalOnlyProvider.ID to {
|
||||
LocalOnlyProvider(
|
||||
java.io.File(appFilesDir, "shonar-local"),
|
||||
pathStore = plainStore,
|
||||
)
|
||||
},
|
||||
CUSTOM_SHONAR_ID to { custom },
|
||||
NEXTCLOUD_ID to { nextcloud },
|
||||
SYNC_FOLDER_ID to { folder },
|
||||
// P6: start9 / umbrel platform probes
|
||||
),
|
||||
tls = tlsPolicy,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue