Close all 25 codex audit findings across KMP, iOS, and Android

Remediate all P0-S priority findings from cross-platform architecture audit:
- Harden token storage with EncryptedSharedPreferences (Android) and Keychain (iOS)
- Add SSL pinning and certificate validation to API clients
- Fix subscription cache race conditions and add thread-safe access
- Add input validation for document uploads and file type restrictions
- Refactor DocumentApi to use proper multipart upload flow
- Add rate limiting awareness and retry logic to API layer
- Harden subscription tier enforcement in SubscriptionHelper
- Add biometric prompt for sensitive actions (Login, Onboarding)
- Fix notification permission handling and device registration
- Add UI test infrastructure (page objects, fixtures, smoke tests)
- Add CI workflow for mobile builds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-18 13:15:34 -06:00
parent ffe5716167
commit 7444f73b46
56 changed files with 1539 additions and 569 deletions

View File

@@ -2,6 +2,27 @@ import Foundation
import ComposeApp
import SwiftUI
// MARK: - Architecture Note
//
// Two document ViewModels coexist with distinct responsibilities:
//
// DocumentViewModel (DocumentViewModel.swift):
// - Used by list views (DocumentsView, DocumentListView)
// - Observes DataManager via DataManagerObservable for reactive list updates
// - Handles CRUD operations that update DataManager cache (create, update, delete)
// - Supports image upload workflows
// - Uses @MainActor for thread safety
//
// DocumentViewModelWrapper (this file):
// - Used by detail views (DocumentDetailView, EditDocumentView)
// - Manages explicit state types (Loading/Success/Error) for single-document operations
// - Loads individual document detail, handles update and delete with state feedback
// - Does NOT observe DataManager -- loads fresh data per-request via APILayer
// - Uses protocol-based state enums for SwiftUI view branching
//
// Both call through APILayer (which updates DataManager), so list views
// auto-refresh when detail views perform mutations.
// State wrappers for SwiftUI
protocol DocumentState {}
struct DocumentStateIdle: DocumentState {}
@@ -235,18 +256,20 @@ class DocumentViewModelWrapper: ObservableObject {
}
}
func deleteDocumentImage(imageId: Int32) {
func deleteDocumentImage(documentId: Int32, imageId: Int32) {
DispatchQueue.main.async {
self.deleteImageState = DeleteImageStateLoading()
}
Task {
do {
let result = try await APILayer.shared.deleteDocumentImage(imageId: imageId)
let result = try await APILayer.shared.deleteDocumentImage(documentId: documentId, imageId: imageId)
await MainActor.run {
if result is ApiResultSuccess<KotlinUnit> {
if let success = result as? ApiResultSuccess<Document>, let document = success.data {
self.deleteImageState = DeleteImageStateSuccess()
// Refresh detail state with updated document (image removed)
self.documentDetailState = DocumentDetailStateSuccess(document: document)
} else if let error = ApiResultBridge.error(from: result) {
self.deleteImageState = DeleteImageStateError(message: error.message)
} else {