82 lines
2.5 KiB
Swift
82 lines
2.5 KiB
Swift
import Foundation
|
|
import TermindDomain
|
|
|
|
import struct TermindDomain.Host
|
|
|
|
/// Incremental persistence boundary for SFTP browse history.
|
|
///
|
|
/// Preview and focused-test instances use the in-memory projection. Each app
|
|
/// supplies a persistent repository so successful directory visits and file
|
|
/// downloads remain available across sessions without entering Vault sync.
|
|
@MainActor
|
|
@Observable
|
|
public final class SFTPHistoryModel {
|
|
public static let perHostLimit = HistoryLimits.retainedEntries
|
|
|
|
private let repository: any SFTPHistoryRepository
|
|
public private(set) var revision: UInt64 = 0
|
|
public private(set) var storageError: String?
|
|
|
|
public init(
|
|
entries: [SFTPHistoryEntry] = [],
|
|
repository: (any SFTPHistoryRepository)? = nil
|
|
) {
|
|
self.repository = repository ?? InMemorySFTPHistoryRepository(entries: entries)
|
|
do {
|
|
try self.repository.trim(to: Self.perHostLimit)
|
|
} catch {
|
|
storageError = error.localizedDescription
|
|
}
|
|
}
|
|
|
|
public func entries(for hostID: Host.ID, matching query: String = "") -> [SFTPHistoryEntry] {
|
|
let values: [SFTPHistoryEntry]
|
|
do {
|
|
values = try repository.entries(for: hostID)
|
|
storageError = nil
|
|
} catch {
|
|
storageError = error.localizedDescription
|
|
return []
|
|
}
|
|
guard !query.isEmpty else { return values }
|
|
return values.filter { $0.path.localizedCaseInsensitiveContains(query) }
|
|
}
|
|
|
|
public func record(
|
|
kind: SFTPHistoryEntry.Kind, path: String, for hostID: Host.ID,
|
|
at date: Date = Date()
|
|
) {
|
|
let path = RemotePath.normalized(path)
|
|
do {
|
|
try repository.upsert(
|
|
SFTPHistoryEntry(hostID: hostID, kind: kind, path: path, visitedAt: date),
|
|
limit: Self.perHostLimit
|
|
)
|
|
storageError = nil
|
|
revision &+= 1
|
|
} catch {
|
|
storageError = error.localizedDescription
|
|
}
|
|
}
|
|
|
|
public func delete(_ id: SFTPHistoryEntry.ID) {
|
|
do {
|
|
try repository.delete(id)
|
|
storageError = nil
|
|
revision &+= 1
|
|
} catch {
|
|
storageError = error.localizedDescription
|
|
}
|
|
}
|
|
|
|
public func clear(for hostID: Host.ID) {
|
|
do {
|
|
try repository.clear(for: hostID)
|
|
storageError = nil
|
|
revision &+= 1
|
|
} catch {
|
|
storageError = error.localizedDescription
|
|
}
|
|
}
|
|
}
|