@capgo/capacitor-updater 6.0.0 → 6.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +530 -210
- package/android/build.gradle +3 -3
- package/android/proguard-rules.pro +28 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/Callback.java +13 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +315 -259
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +716 -411
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +4 -12
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +3 -2
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +31 -22
- package/android/src/main/java/ee/forgr/capacitor_updater/InternalUtils.java +41 -0
- package/dist/docs.json +884 -329
- package/dist/esm/definitions.d.ts +402 -242
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +21 -40
- package/dist/esm/web.js +21 -1
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +21 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +21 -1
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/CapacitorUpdater.swift +226 -69
- package/ios/Plugin/CapacitorUpdaterPlugin.m +4 -1
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +227 -59
- package/ios/Plugin/CryptoCipher.swift +9 -3
- package/package.json +21 -24
|
@@ -17,11 +17,6 @@ extension URL {
|
|
|
17
17
|
return FileManager().fileExists(atPath: self.path)
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
extension Date {
|
|
21
|
-
func adding(minutes: Int) -> Date {
|
|
22
|
-
return Calendar.current.date(byAdding: .minute, value: minutes, to: self)!
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
20
|
struct SetChannelDec: Decodable {
|
|
26
21
|
let status: String?
|
|
27
22
|
let error: String?
|
|
@@ -78,12 +73,14 @@ struct InfoObject: Codable {
|
|
|
78
73
|
let version_build: String?
|
|
79
74
|
let version_code: String?
|
|
80
75
|
let version_os: String?
|
|
81
|
-
|
|
76
|
+
var version_name: String?
|
|
77
|
+
var old_version_name: String?
|
|
82
78
|
let plugin_version: String?
|
|
83
79
|
let is_emulator: Bool?
|
|
84
80
|
let is_prod: Bool?
|
|
85
81
|
var action: String?
|
|
86
82
|
var channel: String?
|
|
83
|
+
var defaultChannel: String?
|
|
87
84
|
}
|
|
88
85
|
struct AppVersionDec: Decodable {
|
|
89
86
|
let version: String?
|
|
@@ -93,6 +90,7 @@ struct AppVersionDec: Decodable {
|
|
|
93
90
|
let error: String?
|
|
94
91
|
let session_key: String?
|
|
95
92
|
let major: Bool?
|
|
93
|
+
let data: [String: String]?
|
|
96
94
|
}
|
|
97
95
|
public class AppVersion: NSObject {
|
|
98
96
|
var version: String = ""
|
|
@@ -102,6 +100,7 @@ public class AppVersion: NSObject {
|
|
|
102
100
|
var error: String?
|
|
103
101
|
var sessionKey: String?
|
|
104
102
|
var major: Bool?
|
|
103
|
+
var data: [String: String]?
|
|
105
104
|
}
|
|
106
105
|
|
|
107
106
|
extension AppVersion {
|
|
@@ -223,21 +222,22 @@ extension CustomError: LocalizedError {
|
|
|
223
222
|
private let versionOs = UIDevice.current.systemVersion
|
|
224
223
|
private let documentsDir: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
225
224
|
private let libraryDir: URL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!
|
|
226
|
-
private let bundleDirectoryHot: String = "versions"
|
|
227
225
|
private let DEFAULT_FOLDER: String = ""
|
|
228
226
|
private let bundleDirectory: String = "NoCloud/ionic_built_snapshots"
|
|
229
227
|
private let INFO_SUFFIX: String = "_info"
|
|
230
228
|
private let FALLBACK_VERSION: String = "pastVersion"
|
|
231
229
|
private let NEXT_VERSION: String = "nextVersion"
|
|
230
|
+
private var unzipPercent = 0
|
|
232
231
|
|
|
233
232
|
public let TAG: String = "✨ Capacitor-updater:"
|
|
234
233
|
public let CAP_SERVER_PATH: String = "serverBasePath"
|
|
235
|
-
public var
|
|
234
|
+
public var versionBuild: String = ""
|
|
236
235
|
public var customId: String = ""
|
|
237
236
|
public var PLUGIN_VERSION: String = ""
|
|
238
|
-
public
|
|
237
|
+
public var timeout: Double = 20
|
|
239
238
|
public var statsUrl: String = ""
|
|
240
239
|
public var channelUrl: String = ""
|
|
240
|
+
public var defaultChannel: String = ""
|
|
241
241
|
public var appId: String = ""
|
|
242
242
|
public var deviceID = UIDevice.current.identifierForVendor?.uuidString ?? ""
|
|
243
243
|
public var privateKey: String = ""
|
|
@@ -295,8 +295,7 @@ extension CustomError: LocalizedError {
|
|
|
295
295
|
return false
|
|
296
296
|
#endif
|
|
297
297
|
}
|
|
298
|
-
//
|
|
299
|
-
// Hot Reload path /var/mobile/Containers/Data/Application/8C0C07BE-0FD3-4FD4-B7DF-90A88E12B8C3/Documents/FOLDER
|
|
298
|
+
// Hot Reload path /var/mobile/Containers/Data/Application/8C0C07BE-0FD3-4FD4-B7DF-90A88E12B8C3/Library/NoCloud/ionic_built_snapshots/FOLDER
|
|
300
299
|
// Normal /private/var/containers/Bundle/Application/8C0C07BE-0FD3-4FD4-B7DF-90A88E12B8C3/App.app/public
|
|
301
300
|
|
|
302
301
|
private func prepareFolder(source: URL) throws {
|
|
@@ -337,9 +336,27 @@ extension CustomError: LocalizedError {
|
|
|
337
336
|
}
|
|
338
337
|
|
|
339
338
|
private func getChecksum(filePath: URL) -> String {
|
|
339
|
+
let bufferSize = 1024 * 1024 * 5 // 5 MB
|
|
340
|
+
var checksum = uLong(0)
|
|
341
|
+
|
|
340
342
|
do {
|
|
341
|
-
let
|
|
342
|
-
|
|
343
|
+
let fileHandle = try FileHandle(forReadingFrom: filePath)
|
|
344
|
+
defer {
|
|
345
|
+
fileHandle.closeFile()
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
while autoreleasepool(invoking: {
|
|
349
|
+
let fileData = fileHandle.readData(ofLength: bufferSize)
|
|
350
|
+
if fileData.count > 0 {
|
|
351
|
+
checksum = fileData.withUnsafeBytes {
|
|
352
|
+
crc32(checksum, $0.bindMemory(to: Bytef.self).baseAddress, uInt(fileData.count))
|
|
353
|
+
}
|
|
354
|
+
return true // Continue
|
|
355
|
+
} else {
|
|
356
|
+
return false // End of file
|
|
357
|
+
}
|
|
358
|
+
}) {}
|
|
359
|
+
|
|
343
360
|
return String(format: "%08X", checksum).lowercased()
|
|
344
361
|
} catch {
|
|
345
362
|
print("\(self.TAG) Cannot get checksum: \(filePath.path)", error)
|
|
@@ -348,7 +365,7 @@ extension CustomError: LocalizedError {
|
|
|
348
365
|
}
|
|
349
366
|
|
|
350
367
|
private func decryptFile(filePath: URL, sessionKey: String, version: String) throws {
|
|
351
|
-
if self.privateKey.isEmpty || sessionKey.isEmpty {
|
|
368
|
+
if self.privateKey.isEmpty || sessionKey.isEmpty || sessionKey.components(separatedBy: ":").count != 2 {
|
|
352
369
|
print("\(self.TAG) Cannot found privateKey or sessionKey")
|
|
353
370
|
return
|
|
354
371
|
}
|
|
@@ -363,11 +380,24 @@ extension CustomError: LocalizedError {
|
|
|
363
380
|
print("cannot decode sessionKey", sessionKey)
|
|
364
381
|
throw CustomError.cannotDecode
|
|
365
382
|
}
|
|
366
|
-
|
|
367
|
-
let
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
383
|
+
|
|
384
|
+
guard let sessionKeyDataEncrypted = Data(base64Encoded: sessionKeyArray[1]) else {
|
|
385
|
+
throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
guard let sessionKeyDataDecrypted = rsaPrivateKey.decrypt(data: sessionKeyDataEncrypted) else {
|
|
389
|
+
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
let aesPrivateKey = AES128Key(iv: ivData, aes128Key: sessionKeyDataDecrypted)
|
|
393
|
+
|
|
394
|
+
guard let encryptedData = try? Data(contentsOf: filePath) else {
|
|
395
|
+
throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
guard let decryptedData = aesPrivateKey.decrypt(data: encryptedData) else {
|
|
399
|
+
throw NSError(domain: "Failed to decrypt data", code: 4, userInfo: nil)
|
|
400
|
+
}
|
|
371
401
|
|
|
372
402
|
try decryptedData.write(to: filePath)
|
|
373
403
|
} catch {
|
|
@@ -377,13 +407,80 @@ extension CustomError: LocalizedError {
|
|
|
377
407
|
}
|
|
378
408
|
}
|
|
379
409
|
|
|
380
|
-
private func
|
|
410
|
+
private func unzipProgressHandler(entry: String, zipInfo: unz_file_info, entryNumber: Int, total: Int, destUnZip: URL, id: String, unzipError: inout NSError?) {
|
|
411
|
+
if entry.contains("\\") {
|
|
412
|
+
print("\(self.TAG) unzip: Windows path is not supported, please use unix path as required by zip RFC: \(entry)")
|
|
413
|
+
self.sendStats(action: "windows_path_fail")
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
let fileURL = destUnZip.appendingPathComponent(entry)
|
|
417
|
+
let canonicalPath = fileURL.path
|
|
418
|
+
let canonicalDir = destUnZip.path
|
|
419
|
+
|
|
420
|
+
if !canonicalPath.hasPrefix(canonicalDir) {
|
|
421
|
+
self.sendStats(action: "canonical_path_fail")
|
|
422
|
+
unzipError = NSError(domain: "CanonicalPathError", code: 0, userInfo: nil)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
let isDirectory = entry.hasSuffix("/")
|
|
426
|
+
if !isDirectory {
|
|
427
|
+
let folderURL = fileURL.deletingLastPathComponent()
|
|
428
|
+
if !FileManager.default.fileExists(atPath: folderURL.path) {
|
|
429
|
+
do {
|
|
430
|
+
try FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
|
|
431
|
+
} catch {
|
|
432
|
+
self.sendStats(action: "directory_path_fail")
|
|
433
|
+
unzipError = error as NSError
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
let newPercent = self.calcTotalPercent(percent: Int(Double(entryNumber) / Double(total) * 100), min: 75, max: 81)
|
|
439
|
+
if newPercent != self.unzipPercent {
|
|
440
|
+
self.unzipPercent = newPercent
|
|
441
|
+
self.notifyDownload(id, newPercent)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
private func saveDownloaded(sourceZip: URL, id: String, base: URL, notify: Bool) throws {
|
|
381
446
|
try prepareFolder(source: base)
|
|
382
447
|
let destHot: URL = base.appendingPathComponent(id)
|
|
383
448
|
let destUnZip: URL = documentsDir.appendingPathComponent(randomString(length: 10))
|
|
384
|
-
|
|
385
|
-
|
|
449
|
+
|
|
450
|
+
self.unzipPercent = 0
|
|
451
|
+
self.notifyDownload(id, 75)
|
|
452
|
+
|
|
453
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
454
|
+
var unzipError: NSError?
|
|
455
|
+
|
|
456
|
+
let success = SSZipArchive.unzipFile(atPath: sourceZip.path,
|
|
457
|
+
toDestination: destUnZip.path,
|
|
458
|
+
preserveAttributes: true,
|
|
459
|
+
overwrite: true,
|
|
460
|
+
nestedZipLevel: 1,
|
|
461
|
+
password: nil,
|
|
462
|
+
error: &unzipError,
|
|
463
|
+
delegate: nil,
|
|
464
|
+
progressHandler: { [weak self] (entry, zipInfo, entryNumber, total) in
|
|
465
|
+
DispatchQueue.global(qos: .background).async {
|
|
466
|
+
guard let self = self else { return }
|
|
467
|
+
if !notify {
|
|
468
|
+
return
|
|
469
|
+
}
|
|
470
|
+
self.unzipProgressHandler(entry: entry, zipInfo: zipInfo, entryNumber: entryNumber, total: total, destUnZip: destUnZip, id: id, unzipError: &unzipError)
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
completionHandler: { _, _, _ in
|
|
474
|
+
semaphore.signal()
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
semaphore.wait()
|
|
478
|
+
|
|
479
|
+
if !success || unzipError != nil {
|
|
480
|
+
self.sendStats(action: "unzip_fail")
|
|
481
|
+
throw unzipError ?? CustomError.cannotUnzip
|
|
386
482
|
}
|
|
483
|
+
|
|
387
484
|
if try unflatFolder(source: destUnZip, dest: destHot) {
|
|
388
485
|
try deleteFolder(source: destUnZip)
|
|
389
486
|
}
|
|
@@ -395,7 +492,7 @@ extension CustomError: LocalizedError {
|
|
|
395
492
|
device_id: self.deviceID,
|
|
396
493
|
app_id: self.appId,
|
|
397
494
|
custom_id: self.customId,
|
|
398
|
-
version_build: self.
|
|
495
|
+
version_build: self.versionBuild,
|
|
399
496
|
version_code: self.versionCode,
|
|
400
497
|
version_os: self.versionOs,
|
|
401
498
|
version_name: self.getCurrentBundle().getVersionName(),
|
|
@@ -403,7 +500,8 @@ extension CustomError: LocalizedError {
|
|
|
403
500
|
is_emulator: self.isEmulator(),
|
|
404
501
|
is_prod: self.isProd(),
|
|
405
502
|
action: nil,
|
|
406
|
-
channel: nil
|
|
503
|
+
channel: nil,
|
|
504
|
+
defaultChannel: self.defaultChannel
|
|
407
505
|
)
|
|
408
506
|
}
|
|
409
507
|
|
|
@@ -438,6 +536,9 @@ extension CustomError: LocalizedError {
|
|
|
438
536
|
if let sessionKey = response.value?.session_key {
|
|
439
537
|
latest.sessionKey = sessionKey
|
|
440
538
|
}
|
|
539
|
+
if let data = response.value?.data {
|
|
540
|
+
latest.data = data
|
|
541
|
+
}
|
|
441
542
|
case let .failure(error):
|
|
442
543
|
print("\(self.TAG) Error getting Latest", response.value ?? "", error )
|
|
443
544
|
latest.message = "Error getting Latest \(String(describing: response.value))"
|
|
@@ -473,7 +574,7 @@ extension CustomError: LocalizedError {
|
|
|
473
574
|
let percent = self.calcTotalPercent(percent: Int(progress.fractionCompleted * 100), min: 10, max: 70)
|
|
474
575
|
self.notifyDownload(id, percent)
|
|
475
576
|
}
|
|
476
|
-
request.responseURL { (response) in
|
|
577
|
+
request.responseURL(queue: .global(qos: .background), completionHandler: { (response) in
|
|
477
578
|
if let fileURL = response.fileURL {
|
|
478
579
|
switch response.result {
|
|
479
580
|
case .success:
|
|
@@ -481,27 +582,43 @@ extension CustomError: LocalizedError {
|
|
|
481
582
|
do {
|
|
482
583
|
try self.decryptFile(filePath: fileURL, sessionKey: sessionKey, version: version)
|
|
483
584
|
checksum = self.getChecksum(filePath: fileURL)
|
|
484
|
-
try self.saveDownloaded(sourceZip: fileURL, id: id, base: self.
|
|
485
|
-
self.notifyDownload(id, 85)
|
|
486
|
-
try self.saveDownloaded(sourceZip: fileURL, id: id, base: self.libraryDir.appendingPathComponent(self.bundleDirectory))
|
|
487
|
-
self.notifyDownload(id, 100)
|
|
585
|
+
try self.saveDownloaded(sourceZip: fileURL, id: id, base: self.libraryDir.appendingPathComponent(self.bundleDirectory), notify: true)
|
|
488
586
|
try self.deleteFolder(source: fileURL)
|
|
587
|
+
self.notifyDownload(id, 100)
|
|
489
588
|
} catch {
|
|
490
589
|
print("\(self.TAG) download unzip error", error)
|
|
491
590
|
mainError = error as NSError
|
|
492
591
|
}
|
|
493
592
|
case let .failure(error):
|
|
494
593
|
print("\(self.TAG) download error", response.value ?? "", error)
|
|
594
|
+
if let afError = error as? AFError,
|
|
595
|
+
case .sessionTaskFailed(let urlError as URLError) = afError,
|
|
596
|
+
urlError.code == .cannotWriteToFile {
|
|
597
|
+
self.sendStats(action: "low_mem_fail", versionName: version)
|
|
598
|
+
}
|
|
495
599
|
mainError = error as NSError
|
|
496
600
|
}
|
|
497
601
|
}
|
|
498
602
|
semaphore.signal()
|
|
499
|
-
}
|
|
603
|
+
})
|
|
500
604
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.DOWNLOADING, downloaded: Date(), checksum: checksum))
|
|
501
605
|
self.notifyDownload(id, 0)
|
|
606
|
+
let reachabilityManager = NetworkReachabilityManager()
|
|
607
|
+
reachabilityManager?.startListening { status in
|
|
608
|
+
switch status {
|
|
609
|
+
case .notReachable:
|
|
610
|
+
// Stop the download request if the network is not reachable
|
|
611
|
+
request.cancel()
|
|
612
|
+
mainError = NSError(domain: NSURLErrorDomain, code: NSURLErrorNotConnectedToInternet, userInfo: nil)
|
|
613
|
+
semaphore.signal()
|
|
614
|
+
default:
|
|
615
|
+
break
|
|
616
|
+
}
|
|
617
|
+
}
|
|
502
618
|
semaphore.wait()
|
|
503
|
-
|
|
504
|
-
|
|
619
|
+
reachabilityManager?.stopListening()
|
|
620
|
+
if let error = mainError {
|
|
621
|
+
throw error
|
|
505
622
|
}
|
|
506
623
|
let info: BundleInfo = BundleInfo(id: id, version: version, status: BundleStatus.PENDING, downloaded: Date(), checksum: checksum)
|
|
507
624
|
self.saveBundleInfo(id: id, bundle: info)
|
|
@@ -509,7 +626,7 @@ extension CustomError: LocalizedError {
|
|
|
509
626
|
}
|
|
510
627
|
|
|
511
628
|
public func list() -> [BundleInfo] {
|
|
512
|
-
let dest: URL = documentsDir.appendingPathComponent(
|
|
629
|
+
let dest: URL = documentsDir.appendingPathComponent(bundleDirectory)
|
|
513
630
|
do {
|
|
514
631
|
let files: [String] = try FileManager.default.contentsOfDirectory(atPath: dest.path)
|
|
515
632
|
var res: [BundleInfo] = []
|
|
@@ -532,17 +649,11 @@ extension CustomError: LocalizedError {
|
|
|
532
649
|
print("\(self.TAG) Cannot delete \(id)")
|
|
533
650
|
return false
|
|
534
651
|
}
|
|
535
|
-
let destHot: URL =
|
|
536
|
-
let destPersist: URL = libraryDir.appendingPathComponent(bundleDirectory).appendingPathComponent(id)
|
|
652
|
+
let destHot: URL = libraryDir.appendingPathComponent(bundleDirectory).appendingPathComponent(id)
|
|
537
653
|
do {
|
|
538
654
|
try FileManager.default.removeItem(atPath: destHot.path)
|
|
539
655
|
} catch {
|
|
540
|
-
print("\(self.TAG)
|
|
541
|
-
}
|
|
542
|
-
do {
|
|
543
|
-
try FileManager.default.removeItem(atPath: destPersist.path)
|
|
544
|
-
} catch {
|
|
545
|
-
print("\(self.TAG) Folder \(destPersist.path), not removed.")
|
|
656
|
+
print("\(self.TAG) Folder \(destHot.path), not removed.")
|
|
546
657
|
return false
|
|
547
658
|
}
|
|
548
659
|
if removeInfo {
|
|
@@ -568,13 +679,15 @@ extension CustomError: LocalizedError {
|
|
|
568
679
|
}
|
|
569
680
|
|
|
570
681
|
private func bundleExists(id: String) -> Bool {
|
|
571
|
-
let destHot: URL = self.
|
|
572
|
-
let destHotPersist: URL = self.getPathPersist(id: id)
|
|
682
|
+
let destHot: URL = self.getBundleDirectory(id: id)
|
|
573
683
|
let indexHot: URL = destHot.appendingPathComponent("index.html")
|
|
574
|
-
let indexPersist: URL = destHotPersist.appendingPathComponent("index.html")
|
|
575
|
-
let url: URL = self.getBundleDirectory(id: id)
|
|
576
684
|
let bundleIndo: BundleInfo = self.getBundleInfo(id: id)
|
|
577
|
-
if
|
|
685
|
+
if
|
|
686
|
+
destHot.exist &&
|
|
687
|
+
destHot.isDirectory &&
|
|
688
|
+
!indexHot.isDirectory &&
|
|
689
|
+
indexHot.exist &&
|
|
690
|
+
!bundleIndo.isDeleted() {
|
|
578
691
|
return true
|
|
579
692
|
}
|
|
580
693
|
return false
|
|
@@ -587,9 +700,10 @@ extension CustomError: LocalizedError {
|
|
|
587
700
|
return true
|
|
588
701
|
}
|
|
589
702
|
if bundleExists(id: id) {
|
|
703
|
+
let currentBundleName = self.getCurrentBundle().getVersionName()
|
|
590
704
|
self.setCurrentBundle(bundle: self.getBundleDirectory(id: id).path)
|
|
591
705
|
self.setBundleStatus(id: id, status: BundleStatus.PENDING)
|
|
592
|
-
self.sendStats(action: "set", versionName: newBundle.getVersionName())
|
|
706
|
+
self.sendStats(action: "set", versionName: newBundle.getVersionName(), oldVersionName: currentBundleName)
|
|
593
707
|
return true
|
|
594
708
|
}
|
|
595
709
|
self.setBundleStatus(id: id, status: BundleStatus.ERROR)
|
|
@@ -597,12 +711,12 @@ extension CustomError: LocalizedError {
|
|
|
597
711
|
return false
|
|
598
712
|
}
|
|
599
713
|
|
|
600
|
-
public func
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
714
|
+
public func autoReset() {
|
|
715
|
+
let currentBundle: BundleInfo = self.getCurrentBundle()
|
|
716
|
+
if !currentBundle.isBuiltin() && !self.bundleExists(id: currentBundle.getId()) {
|
|
717
|
+
print("\(self.TAG) Folder at bundle path does not exist. Triggering reset.")
|
|
718
|
+
self.reset()
|
|
719
|
+
}
|
|
606
720
|
}
|
|
607
721
|
|
|
608
722
|
public func reset() {
|
|
@@ -611,11 +725,12 @@ extension CustomError: LocalizedError {
|
|
|
611
725
|
|
|
612
726
|
public func reset(isInternal: Bool) {
|
|
613
727
|
print("\(self.TAG) reset: \(isInternal)")
|
|
728
|
+
let currentBundleName = self.getCurrentBundle().getVersionName()
|
|
614
729
|
self.setCurrentBundle(bundle: "")
|
|
615
730
|
self.setFallbackBundle(fallback: Optional<BundleInfo>.none)
|
|
616
731
|
_ = self.setNextBundle(next: Optional<String>.none)
|
|
617
732
|
if !isInternal {
|
|
618
|
-
self.sendStats(action: "reset", versionName: self.getCurrentBundle().getVersionName())
|
|
733
|
+
self.sendStats(action: "reset", versionName: self.getCurrentBundle().getVersionName(), oldVersionName: currentBundleName)
|
|
619
734
|
}
|
|
620
735
|
}
|
|
621
736
|
|
|
@@ -639,6 +754,42 @@ extension CustomError: LocalizedError {
|
|
|
639
754
|
self.setBundleStatus(id: bundle.getId(), status: BundleStatus.ERROR)
|
|
640
755
|
}
|
|
641
756
|
|
|
757
|
+
func unsetChannel() -> SetChannel {
|
|
758
|
+
let setChannel: SetChannel = SetChannel()
|
|
759
|
+
if (self.channelUrl ).isEmpty {
|
|
760
|
+
print("\(self.TAG) Channel URL is not set")
|
|
761
|
+
setChannel.message = "Channel URL is not set"
|
|
762
|
+
setChannel.error = "missing_config"
|
|
763
|
+
return setChannel
|
|
764
|
+
}
|
|
765
|
+
let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0)
|
|
766
|
+
let parameters: InfoObject = self.createInfoObject()
|
|
767
|
+
|
|
768
|
+
let request = AF.request(self.channelUrl, method: .delete, parameters: parameters, encoder: JSONParameterEncoder.default, requestModifier: { $0.timeoutInterval = self.timeout })
|
|
769
|
+
|
|
770
|
+
request.validate().responseDecodable(of: SetChannelDec.self) { response in
|
|
771
|
+
switch response.result {
|
|
772
|
+
case .success:
|
|
773
|
+
if let status = response.value?.status {
|
|
774
|
+
setChannel.status = status
|
|
775
|
+
}
|
|
776
|
+
if let error = response.value?.error {
|
|
777
|
+
setChannel.error = error
|
|
778
|
+
}
|
|
779
|
+
if let message = response.value?.message {
|
|
780
|
+
setChannel.message = message
|
|
781
|
+
}
|
|
782
|
+
case let .failure(error):
|
|
783
|
+
print("\(self.TAG) Error unset Channel", response.value ?? "", error)
|
|
784
|
+
setChannel.message = "Error unset Channel \(String(describing: response.value))"
|
|
785
|
+
setChannel.error = "response_error"
|
|
786
|
+
}
|
|
787
|
+
semaphore.signal()
|
|
788
|
+
}
|
|
789
|
+
semaphore.wait()
|
|
790
|
+
return setChannel
|
|
791
|
+
}
|
|
792
|
+
|
|
642
793
|
func setChannel(channel: String) -> SetChannel {
|
|
643
794
|
let setChannel: SetChannel = SetChannel()
|
|
644
795
|
if (self.channelUrl ).isEmpty {
|
|
@@ -717,18 +868,31 @@ extension CustomError: LocalizedError {
|
|
|
717
868
|
return getChannel
|
|
718
869
|
}
|
|
719
870
|
|
|
720
|
-
func sendStats(action: String, versionName: String) {
|
|
721
|
-
|
|
871
|
+
func sendStats(action: String, versionName: String? = nil, oldVersionName: String? = "") {
|
|
872
|
+
guard !statsUrl.isEmpty else {
|
|
722
873
|
return
|
|
723
874
|
}
|
|
724
|
-
|
|
875
|
+
|
|
876
|
+
let versionName = versionName ?? getCurrentBundle().getVersionName()
|
|
877
|
+
|
|
878
|
+
var parameters = createInfoObject()
|
|
725
879
|
parameters.action = action
|
|
880
|
+
parameters.version_name = versionName
|
|
881
|
+
parameters.old_version_name = oldVersionName
|
|
882
|
+
|
|
726
883
|
DispatchQueue.global(qos: .background).async {
|
|
727
|
-
let request = AF.request(
|
|
884
|
+
let request = AF.request(
|
|
885
|
+
self.statsUrl,
|
|
886
|
+
method: .post,
|
|
887
|
+
parameters: parameters,
|
|
888
|
+
encoder: JSONParameterEncoder.default,
|
|
889
|
+
requestModifier: { $0.timeoutInterval = self.timeout }
|
|
890
|
+
)
|
|
891
|
+
|
|
728
892
|
request.responseData { response in
|
|
729
893
|
switch response.result {
|
|
730
894
|
case .success:
|
|
731
|
-
print("\(self.TAG) Stats
|
|
895
|
+
print("\(self.TAG) Stats sent for \(action), version \(versionName)")
|
|
732
896
|
case let .failure(error):
|
|
733
897
|
print("\(self.TAG) Error sending stats: ", response.value ?? "", error)
|
|
734
898
|
}
|
|
@@ -741,7 +905,7 @@ extension CustomError: LocalizedError {
|
|
|
741
905
|
if id != nil {
|
|
742
906
|
trueId = id!
|
|
743
907
|
}
|
|
744
|
-
print("\(self.TAG) Getting info for bundle [\(trueId)]")
|
|
908
|
+
// print("\(self.TAG) Getting info for bundle [\(trueId)]")
|
|
745
909
|
let result: BundleInfo
|
|
746
910
|
if BundleInfo.ID_BUILTIN == trueId {
|
|
747
911
|
result = BundleInfo(id: trueId, version: "", status: BundleStatus.SUCCESS, checksum: "")
|
|
@@ -773,7 +937,7 @@ extension CustomError: LocalizedError {
|
|
|
773
937
|
self.saveBundleInfo(id: id, bundle: nil)
|
|
774
938
|
}
|
|
775
939
|
|
|
776
|
-
|
|
940
|
+
public func saveBundleInfo(id: String, bundle: BundleInfo?) {
|
|
777
941
|
if bundle != nil && (bundle!.isBuiltin() || bundle!.isUnknown()) {
|
|
778
942
|
print("\(self.TAG) Not saving info for bundle [\(id)]", bundle?.toString() ?? "")
|
|
779
943
|
return
|
|
@@ -793,12 +957,6 @@ extension CustomError: LocalizedError {
|
|
|
793
957
|
UserDefaults.standard.synchronize()
|
|
794
958
|
}
|
|
795
959
|
|
|
796
|
-
public func setVersionName(id: String, version: String) {
|
|
797
|
-
print("\(self.TAG) Setting version for folder [\(id)] to \(version)")
|
|
798
|
-
let info = self.getBundleInfo(id: id)
|
|
799
|
-
self.saveBundleInfo(id: id, bundle: info.setVersionName(version: version))
|
|
800
|
-
}
|
|
801
|
-
|
|
802
960
|
private func setBundleStatus(id: String, status: BundleStatus) {
|
|
803
961
|
print("\(self.TAG) Setting status for bundle [\(id)] to \(status)")
|
|
804
962
|
let info = self.getBundleInfo(id: id)
|
|
@@ -846,8 +1004,7 @@ extension CustomError: LocalizedError {
|
|
|
846
1004
|
return false
|
|
847
1005
|
}
|
|
848
1006
|
let newBundle: BundleInfo = self.getBundleInfo(id: nextId)
|
|
849
|
-
|
|
850
|
-
if !newBundle.isBuiltin() && !bundle.exist {
|
|
1007
|
+
if !newBundle.isBuiltin() && !self.bundleExists(id: nextId) {
|
|
851
1008
|
return false
|
|
852
1009
|
}
|
|
853
1010
|
UserDefaults.standard.set(nextId, forKey: self.NEXT_VERSION)
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
// each method the plugin supports using the CAP_PLUGIN_METHOD macro.
|
|
6
6
|
CAP_PLUGIN(CapacitorUpdaterPlugin, "CapacitorUpdater",
|
|
7
7
|
CAP_PLUGIN_METHOD(download, CAPPluginReturnPromise);
|
|
8
|
+
CAP_PLUGIN_METHOD(setUpdateUrl, CAPPluginReturnPromise);
|
|
9
|
+
CAP_PLUGIN_METHOD(setStatsUrl, CAPPluginReturnPromise);
|
|
10
|
+
CAP_PLUGIN_METHOD(setChannelUrl, CAPPluginReturnPromise);
|
|
8
11
|
CAP_PLUGIN_METHOD(set, CAPPluginReturnPromise);
|
|
9
12
|
CAP_PLUGIN_METHOD(list, CAPPluginReturnPromise);
|
|
10
13
|
CAP_PLUGIN_METHOD(delete, CAPPluginReturnPromise);
|
|
@@ -23,5 +26,5 @@ CAP_PLUGIN(CapacitorUpdaterPlugin, "CapacitorUpdater",
|
|
|
23
26
|
CAP_PLUGIN_METHOD(getPluginVersion, CAPPluginReturnPromise);
|
|
24
27
|
CAP_PLUGIN_METHOD(next, CAPPluginReturnPromise);
|
|
25
28
|
CAP_PLUGIN_METHOD(isAutoUpdateEnabled, CAPPluginReturnPromise);
|
|
26
|
-
CAP_PLUGIN_METHOD(
|
|
29
|
+
CAP_PLUGIN_METHOD(getBuiltinVersion, CAPPluginReturnPromise);
|
|
27
30
|
)
|