Files
Kisani/Core/Protocols/NASTransferProtocol.swift

30 lines
945 B
Swift
Raw Normal View History

import Foundation
struct NASItem: Identifiable {
let id = UUID()
let name: String
let path: String
let isDirectory: Bool
let size: Int64
let modifiedDate: Date?
}
protocol NASTransferProtocol: AnyObject {
var isConnected: Bool { get }
func connect(to host: String, port: Int, username: String, password: String) async throws
func disconnect()
func listShares() async throws -> [String]
func listDirectory(at path: String) async throws -> [NASItem]
func createDirectory(at path: String) async throws
func fileExists(at remotePath: String) async throws -> Bool
func upload(
localURL: URL,
remotePath: String,
progress: @escaping (Int64, Int64) -> Void
) async throws
func downloadData(at remotePath: String) async throws -> Data
Add live backup status reconciliation with NAS manifest BackupStatusService (new singleton, ObservableObject, PHPhotoLibraryChangeObserver): - Loads cached BackupStatusSnapshot instantly from UserDefaults on init - Full reconcile: fetch phone assets → connect NAS → load/build manifest → compare → enforce invariant (alreadySafe ≤ phoneTotal), persist result - Debounced (30s) for photo library changes; force=true bypasses debounce - If NAS unreachable: keeps last cached numbers, marks connectionState = .offline - PHPhotoLibraryChangeObserver triggers refresh on any library change BackupManifest (new): - Stored at {remotePath}/.kisani.json on NAS - Indexed by localIdentifier (PHAsset stable ID); filename fallback for legacy entries - Built from directory listing if manifest missing (bootstrap for existing backups) - merged/updated after each backup run via NASTransferProtocol.writeData BackupStatusSnapshot (new): - Single source of truth: phoneTotal, alreadySafe, needBackup (derived), nasArchiveTotal, lastCheckedAt, connectionState - Invariant enforced in service: alreadySafe = min(safe, phoneTotal) Protocol / services: - NASTransferProtocol: adds writeData(_ data: Data, to remotePath: String) - SMBService: implements writeData via temp file + SMBClient.upload - SFTPService: implements writeData via SFTP ByteBuffer write BackupEngine: - Tracks ManifestEntry per successful upload during backup loop - After backup: calls BackupStatusService.refreshAfterBackup(entries:connection:) which applies optimistic UI update then writes manifest + triggers reconcile BackupView: - Reads all stats from BackupStatusService.snapshot (not vm/nasFileCount) - Stats labels: "NAS Archive" / "On iPhone" / "Need Backup" / "Already Safe" - Live refresh triggers: .task (force), scenePhase.active (force), nasReachable change (force), remotePath change (force), backup completed (+2s) - Subtle status row below stats: "Checking…" spinner or "Updated X ago" with wifi-slash icon when NAS offline; tap refresh button for forced reconcile - AppMenuView sheet now correctly passes engine EnvironmentObject BackupViewModel: stripped to auth-only (photosAuthStatus + requestPhotosAccess) Co-Authored-By: Kutesir <kutesir@provoc.ug> Co-Authored-By: Sentry <sentry@provoc.ug>
2026-05-17 12:51:03 +03:00
func writeData(_ data: Data, to remotePath: String) async throws
func deleteFile(at remotePath: String) async throws
}