import CocoaLumberjackSwift
import Foundation
public final class BlobURL: NSObject {
private let serverConnector: ServerConnectorProtocol
private let userSettings: UserSettingsProtocol
private let serverInfoProvider: ServerInfoProvider
private let queue: DispatchQueue
public init(
serverConnector: ServerConnectorProtocol,
userSettings: UserSettingsProtocol,
serverInfoProvider: ServerInfoProvider,
queue: DispatchQueue? = nil
) {
self.serverConnector = serverConnector
self.userSettings = userSettings
self.serverInfoProvider = serverInfoProvider
self.queue = queue ?? DispatchQueue.main
}
@objc public convenience init(
serverConnector: ServerConnectorProtocol,
userSettings: UserSettingsProtocol,
queue: DispatchQueue?
) {
self.init(
serverConnector: serverConnector,
userSettings: userSettings,
serverInfoProvider: ServerInfoProviderFactory.makeServerInfoProvider(),
queue: queue
)
}
@objc public convenience init(
serverConnector: ServerConnectorProtocol,
userSettings: UserSettingsProtocol
) {
self.init(
serverConnector: serverConnector,
userSettings: userSettings,
serverInfoProvider: ServerInfoProviderFactory.makeServerInfoProvider(),
queue: DispatchQueue.main
)
}
public func download(blobID: Data, origin: BlobOrigin, completionHandler: @escaping (URL?, Error?) -> Void) {
genericBlobURL(blobID: blobID, origin: origin, extractURL: { blobServerInfo -> String in
blobServerInfo.downloadURL
}, completionHandler: completionHandler)
}
public func upload(
origin: BlobOrigin,
setPersistParam: Bool,
completionHandler: @escaping (URL?, String?, Error?) -> Void
) {
let persistBlob: Int = {
if setPersistParam {
if origin == .public {
return 1
}
else {
DDLogWarn("Blob persist parameter cannot be enabled if scope is local")
}
}
return 0
}()
genericBlobURL(blobID: nil, origin: origin) { blobServerInfo -> String in
blobServerInfo.uploadURL.replacingOccurrences(of: "{persistBlob}", with: String(persistBlob))
} completionHandler: { url, error in
AuthTokenManager.shared().obtainToken { authToken, err in
self.queue.async {
if error != nil {
completionHandler(nil, nil, err)
}
else {
var authorization: String?
if authToken != nil {
authorization = "Token " + authToken!
}
completionHandler(url, authorization, nil)
}
}
}
}
}
public func done(blobID: Data, origin: BlobOrigin, completionHandler: @escaping (URL?, Error?) -> Void) {
genericBlobURL(blobID: blobID, origin: origin, extractURL: { blobServerInfo -> String in
blobServerInfo.doneURL
}, completionHandler: completionHandler)
}
public func done(blobID: Data, origin: BlobOrigin) async throws -> URL? {
try await withCheckedThrowingContinuation { continuation in
self.done(blobID: blobID, origin: origin) { url, error in
guard error == nil, let url else {
continuation.resume(throwing: error!)
return
}
continuation.resume(returning: url)
}
}
}
private func genericBlobURL(
blobID: Data?,
origin: BlobOrigin,
extractURL: @escaping (BlobServerInfo) -> String,
completionHandler: @escaping (URL?, Error?) -> Void
) {
if let deviceGroupKeys = serverConnector.deviceGroupKeys,
let deviceID = serverConnector.deviceID,
let deviceGroupID = NaClCrypto.shared()?.derivePublicKey(fromSecretKey: deviceGroupKeys.dgpk) {
serverInfoProvider
.mediatorServer(
deviceGroupIDFirstByteHex: deviceGroupKeys
.deviceGroupIDFirstByteHex
) { mediatorServerInfo, err in
if mediatorServerInfo == nil {
completionHandler(nil, err)
return
}
let url = self.substituteBlobID(
url: self.substituteDeviceID(
url: self.substituteOrigin(url: extractURL(mediatorServerInfo!.blob), with: origin),
deviceID: deviceID,
deviceGroupID: deviceGroupID
),
blobID: blobID
)
DDLogDebug("Blob mirror URL: \(url.absoluteString)")
self.queue.async {
completionHandler(url, nil)
}
}
}
else {
serverInfoProvider.blobServer(ipv6: userSettings.enableIPv6) { blobServerInfo, err in
if blobServerInfo == nil {
completionHandler(nil, err)
return
}
let url = self.substituteBlobID(url: extractURL(blobServerInfo!), blobID: blobID)
DDLogDebug("Blob server URL: \(url.absoluteString)")
self.queue.async {
completionHandler(url, nil)
}
}
}
}
private func getBlobIDHex(_ blobID: Data) -> (blobIDHex: String, blobFirstByteHex: String) {
let blobIDHex: String = blobID.hexString
let blobFirstByteHex = String(blobIDHex[...blobIDHex.index(blobIDHex.startIndex, offsetBy: 1)])
return (blobIDHex, blobFirstByteHex)
}
private func substituteBlobID(url: String, blobID: Data?) -> URL {
if let blobID {
let idHex = getBlobIDHex(blobID)
return URL(
string: url.replacingOccurrences(of: "{blobIdPrefix}", with: idHex.blobFirstByteHex)
.replacingOccurrences(of: "{blobId}", with: idHex.blobIDHex)
)!
}
else {
return URL(string: url)!
}
}
private func substituteDeviceID(url: String, deviceID: Data, deviceGroupID: Data) -> String {
url.replacingOccurrences(of: "{deviceId}", with: deviceID.hexString)
.replacingOccurrences(of: "{deviceGroupId}", with: deviceGroupID.hexString)
}
private func substituteOrigin(url: String, with origin: BlobOrigin) -> String {
userSettings.enableMultiDevice ? url.replacingOccurrences(
of: "{origin}",
with: origin.originString
) : url
}
}
¤ Dauer der Verarbeitung: 0.3 Sekunden
¤
*© Formatika GbR, Deutschland
|
|