Fix uploads, add server key entry

This commit is contained in:
Christoph Hagen
2022-06-11 11:27:56 +02:00
parent 093d82893b
commit 2b3ab859fc
10 changed files with 155 additions and 100 deletions

View File

@ -49,6 +49,15 @@ struct Cap {
self.mainImage = data.mainImage
self.classifierVersion = data.classifierVersion
}
var data: CapData {
.init(id: id,
name: name,
count: imageCount,
mainImage: mainImage,
classifierVersion: classifierVersion,
color: color)
}
mutating func update(with data: CapData) {
self.name = data.name

View File

@ -52,10 +52,10 @@ final class Database: ObservableObject {
let serverUrl: URL
@AppStorage("authKey")
private var serverAuthenticationKey: String?
private var serverAuthenticationKey: String = ""
var hasServerAuthentication: Bool {
serverAuthenticationKey != nil
serverAuthenticationKey != ""
}
@Published
@ -120,7 +120,6 @@ final class Database: ObservableObject {
diskCapacity: Database.imageCacheStorage,
directory: cacheDirectory)
loadCaps()
}
@Published
@ -323,7 +322,9 @@ final class Database: ObservableObject {
func save(newCap name: String) -> Cap {
let cap = Cap(id: nextCapId, name: name, classifier: serverClassifierVersion)
caps[cap.id] = cap
#warning("Upload new cap")
DispatchQueue.main.async {
self.changedCaps.insert(cap.id)
}
return cap
}
@ -350,14 +351,9 @@ final class Database: ObservableObject {
}
log("Saved \(url.lastPathComponent) for upload")
caps[capId]?.imageCount += 1
updateImageUploadCounts()
return true
}
private func updateImageUploadCounts() {
}
private func loadImageUploadCounts() -> [Int : Int] {
var result = [Int : Int]()
pendingImageUploads.forEach { url in
@ -376,9 +372,10 @@ final class Database: ObservableObject {
// MARK: Uploads
func startRegularUploads() {
guard uploadTimer != nil else {
guard uploadTimer == nil else {
return
}
log("Starting upload timer")
DispatchQueue.main.async {
self.uploadTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block: self.uploadTimerElapsed)
}
@ -392,13 +389,19 @@ final class Database: ObservableObject {
private func uploadAll() async {
guard !isUploading else {
log("Already uploading")
return
}
DispatchQueue.main.async {
self.isUploading = true
}
await uploadAllChangedCaps()
log("Starting uploads")
let uploaded = await uploadAllChangedCaps()
DispatchQueue.main.async {
self.changedCaps.subtract(uploaded)
}
await uploadAllImages()
log("Uploads finished")
DispatchQueue.main.async {
self.isUploading = false
}
@ -428,18 +431,22 @@ final class Database: ObservableObject {
log("No server authentication to upload to server")
return
}
updateImageUploadCounts()
for url in pendingImageUploads {
guard let capId = capId(from: url) else {
log("Unexpected image \(url.lastPathComponent) in upload folder")
continue
}
guard await upload(imageAt: url, for: capId) else {
guard fm.fileExists(atPath: url.path) else {
log("Missing image \(url.lastPathComponent) in upload folder")
continue
}
guard await upload(imageAt: url, for: capId) else {
log("Failed to upload image \(url.lastPathComponent)")
continue
}
log("Uploaded image \(url.lastPathComponent)")
do {
try fm.removeItem(at: url)
updateImageUploadCounts()
} catch {
log("Failed to remove uploaded image \(url.lastPathComponent): \(error)")
}
@ -448,16 +455,20 @@ final class Database: ObservableObject {
@discardableResult
private func upload(imageAt url: URL, for cap: Int) async -> Bool {
guard let key = serverAuthenticationKey else {
guard hasServerAuthentication else {
return false
}
guard let data = try? Data(contentsOf: url) else {
return false
}
let url = serverUrl
.appendingPathComponent("images")
.appendingPathComponent("\(cap)?key=\(key)")
.appendingPathComponent("\(cap)")
var request = URLRequest(url: url)
request.addValue(serverAuthenticationKey, forHTTPHeaderField: "key")
request.httpMethod = "POST"
do {
let (_, response) = try await URLSession.shared.upload(for: request, fromFile: url)
let (_, response) = try await URLSession.shared.upload(for: request, from: data)
guard let httpResponse = response as? HTTPURLResponse else {
log("Unexpected response for upload of image \(url.lastPathComponent): \(response)")
return false
@ -477,43 +488,45 @@ final class Database: ObservableObject {
changedCaps.count
}
private func uploadAllChangedCaps() async {
private func uploadAllChangedCaps() async -> Set<Int> {
guard hasServerAuthentication else {
log("No server authentication to upload to server")
return
return .init()
}
var uploaded = Set<Int>()
for capId in changedCaps {
guard let cap = caps[capId] else {
log("Missing cap \(capId) to upload")
uploaded.insert(capId)
continue
}
guard await upload(cap: cap) else {
continue
}
log("Uploaded cap \(capId)")
uploaded.insert(capId)
}
changedCaps.subtract(uploaded)
return uploaded
}
@discardableResult
private func upload(cap: Cap) async -> Bool {
guard let key = serverAuthenticationKey else {
guard hasServerAuthentication else {
return false
}
let data: Data
do {
/// `Cap` and `CapData` have equivalent JSON layout
data = try encoder.encode(cap)
data = try encoder.encode(cap.data)
} catch {
log("Failed to encode cap \(cap.id) for upload: \(error)")
return false
}
let url = serverUrl
.appendingPathComponent("images")
.appendingPathComponent("\(cap)?key=\(key)")
.appendingPathComponent("cap")
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue(serverAuthenticationKey, forHTTPHeaderField: "key")
do {
let (_, response) = try await URLSession.shared.upload(for: request, from: data)
guard let httpResponse = response as? HTTPURLResponse else {
@ -524,7 +537,9 @@ final class Database: ObservableObject {
log("Failed to upload cap \(cap.id): Response \(httpResponse.statusCode)")
return false
}
changedCaps.remove(cap.id)
DispatchQueue.main.async {
self.changedCaps.remove(cap.id)
}
return true
} catch {
log("Failed to upload cap \(cap.id): \(error)")