feat(system): multipart backup import via SAF picker — replace path-based import (issue #786) (#839)
* feat(system): multipart backup import via SAF picker — replace path-based import (issue #786) * fix(system): backup trigger sends required JSON body — 422 fix (BackupRequest contract) * fix(system): backup download waits for async archive + saves to Downloads * fix(system): curator min_idle_hours Double parse + single-flight backup download * fix(system): stream backup download to Downloads — @Streaming + chunked MediaStore write (OOM fix) --------- Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
9d0baa78fd
commit
7998248507
8 files changed
+358
-43
No files matched your search
@@ -12,3 +12,13 @@ data class ActionResponse(
|
||||
val uploaded_bytes: Long? = null,
|
||||
val update_command: String? = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* Body for POST /api/ops/backup — the backend requires a JSON body
|
||||
* (BackupRequest; `output` optional). Sending no body at all gets a 422.
|
||||
* Empty object (default) matches the desktop's `{"output": ...}` contract.
|
||||
*/
|
||||
@Serializable
|
||||
data class BackupTriggerRequest(
|
||||
val output: String? = null,
|
||||
)
|
||||
@@ -7,7 +7,9 @@ data class CuratorResponse(
|
||||
val paused: Boolean? = null,
|
||||
val interval_hours: Int? = null,
|
||||
val last_run_at: String? = null,
|
||||
val min_idle_hours: Int? = null,
|
||||
// Backend sends a float (e.g. 2.0) — Int here broke JSON parsing (logcat
|
||||
// "Unexpected symbol '.' in numeric literal at path: $.min_idle_hours").
|
||||
val min_idle_hours: Double? = null,
|
||||
val stale_after_days: Int? = null,
|
||||
val archive_after_days: Int? = null,
|
||||
)
|
||||
@@ -8,6 +8,7 @@ import com.m57.hermescontrol.data.model.AddMcpServerRequest
|
||||
import com.m57.hermescontrol.data.model.AgentPluginInstallBody
|
||||
import com.m57.hermescontrol.data.model.AnalyticsResponse
|
||||
import com.m57.hermescontrol.data.model.AuxiliaryModelsResponse
|
||||
import com.m57.hermescontrol.data.model.BackupTriggerRequest
|
||||
import com.m57.hermescontrol.data.model.BulkDeleteRequest
|
||||
import com.m57.hermescontrol.data.model.BulkDeleteResponse
|
||||
import com.m57.hermescontrol.data.model.CheckpointsResponse
|
||||
@@ -144,6 +145,7 @@ import retrofit2.http.PUT
|
||||
import retrofit2.http.Part
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.Streaming
|
||||
|
||||
interface HermesApiService {
|
||||
@GET("api/skills/content")
|
||||
@@ -750,8 +752,11 @@ interface HermesApiService {
|
||||
@Body request: EnvVarDeleteRequest,
|
||||
): Response<Unit>
|
||||
|
||||
// Backend requires a JSON body (BackupRequest) — bodyless POST 422s.
|
||||
@POST("api/ops/backup")
|
||||
suspend fun triggerBackup(): Response<ActionResponse>
|
||||
suspend fun triggerBackup(
|
||||
@Body body: BackupTriggerRequest,
|
||||
): Response<ActionResponse>
|
||||
|
||||
@POST("api/ops/doctor")
|
||||
suspend fun runDoctor(): Response<DoctorResponse>
|
||||
@@ -869,15 +874,23 @@ interface HermesApiService {
|
||||
): Response<ActionResponse>
|
||||
|
||||
// ── Admin: Backup download ────────────────────────────────────────
|
||||
// @Streaming: backups are 300+ MB — Retrofit's default buffering of
|
||||
// ResponseBody OOMs the app (observed crash with a 327MB archive).
|
||||
@Streaming
|
||||
@GET("api/ops/backup/download")
|
||||
suspend fun downloadBackup(
|
||||
@Query("archive") archive: String,
|
||||
): Response<ResponseBody>
|
||||
|
||||
// ── Admin: Backup import ──────────────────────────────────────────
|
||||
@POST("api/ops/import")
|
||||
suspend fun runImport(
|
||||
@Body body: Map<String, Any>,
|
||||
// Multipart upload (issue #786): the path-based POST /api/ops/import
|
||||
// is useless on a phone (server-side path typed into the app); the
|
||||
// desktop's upload contract is the real one — file + force.
|
||||
@Multipart
|
||||
@POST("api/ops/import-upload")
|
||||
suspend fun importUpload(
|
||||
@Part("force") force: okhttp3.RequestBody,
|
||||
@Part file: okhttp3.MultipartBody.Part,
|
||||
): Response<ActionResponse>
|
||||
|
||||
// ── Admin: Debug share ────────────────────────────────────────────
|
||||
|
||||
Reference in new issue
Block a user