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
77
shared/com/shonar/provider/ShonarProvider.kt
Normal file
77
shared/com/shonar/provider/ShonarProvider.kt
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
package com.shonar.provider
|
||||
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* The single contract between SHONAR and any server (or the device itself).
|
||||
*
|
||||
* Everything the app needs from "the cloud" goes through this interface;
|
||||
* UI, sync engine, and database reference providers by descriptor id only.
|
||||
* LocalOnlyProvider is a first-class implementation, so a missing server is
|
||||
* never a special case.
|
||||
*
|
||||
* Contract rules (enforced by the shared provider contract test suite):
|
||||
* - all suspends are safe to cancel; partial uploads leave no visible object
|
||||
* - upload() is idempotent per RecordingDraft.id (re-upload replaces the
|
||||
* same key, never duplicates)
|
||||
* - originals are immutable after successful upload until delete()
|
||||
* - no method ever logs credentials, tokens, audio bytes, or transcripts
|
||||
* - errors are ProviderError subtypes with secret-free messages
|
||||
*/
|
||||
interface ShonarProvider {
|
||||
|
||||
val descriptor: ProviderDescriptor
|
||||
|
||||
/** Current auth lifecycle; providers push updates here. */
|
||||
val authState: kotlinx.coroutines.flow.StateFlow<AuthState>
|
||||
|
||||
/**
|
||||
* Is there a SHONAR-compatible service at [baseUrl]? Purely read-only;
|
||||
* must not require or request credentials. TlsFailure carries the SPKI
|
||||
* fingerprint so the UI can run explicit trust-on-first-use approval —
|
||||
* never silently accept.
|
||||
*/
|
||||
suspend fun probe(baseUrl: ServerUrl): ProbeResult
|
||||
|
||||
/** Validate [credential] against the server, then hand it to the secure store. */
|
||||
suspend fun connect(credential: ProviderCredential): Unit
|
||||
|
||||
/** Re-validate stored credential (app start / after network return). */
|
||||
suspend fun reconnect(): AuthState
|
||||
|
||||
/**
|
||||
* Disconnect locally; if [revokeOnServer] and the provider supports it,
|
||||
* revoke the credential server-side first (best effort — local state is
|
||||
* cleared even if revoke fails, with the failure surfaced).
|
||||
*/
|
||||
suspend fun disconnect(revokeOnServer: Boolean)
|
||||
|
||||
/** Provider-native account/data deletion, then wipe local state. */
|
||||
suspend fun deleteAccountAndData()
|
||||
|
||||
// ---- storage -----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Upload the original audio for [draft], reporting [onProgress] 0..1.
|
||||
* Implementations use chunked/resumable transfers when the capability is
|
||||
* advertised. Returns the ref for later download/delete/sidecars.
|
||||
*/
|
||||
suspend fun upload(draft: RecordingDraft, onProgress: (Float) -> Unit): RemoteRef
|
||||
|
||||
suspend fun download(ref: RemoteRef, dest: File, onProgress: (Float) -> Unit)
|
||||
|
||||
suspend fun delete(ref: RemoteRef)
|
||||
|
||||
suspend fun list(cursor: String?): Page<RemoteRecording>
|
||||
|
||||
// ---- sidecars (transcript/summary/... JSON, synced independently) ------
|
||||
|
||||
suspend fun putSidecar(ref: RemoteRef, kind: SidecarKind, bytes: ByteArray)
|
||||
|
||||
/** null when the sidecar does not exist remotely. */
|
||||
suspend fun getSidecar(ref: RemoteRef, kind: SidecarKind): ByteArray?
|
||||
|
||||
// ---- status ------------------------------------------------------------
|
||||
|
||||
suspend fun storageLocationSummary(): StorageLocation
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue