react-native-mosquito-transport 0.0.48 → 0.0.49
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/CONTRIBUTING.md +37 -19
- package/MosquitoTransport.podspec +20 -0
- package/android/build.gradle +32 -42
- package/android/src/main/AndroidManifest.xml +1 -3
- package/android/src/main/java/com/mosquitotransport/MosquitoTransportModule.kt +101 -0
- package/android/src/main/java/com/mosquitotransport/MosquitoTransportPackage.kt +31 -0
- package/android/src/main/java/com/mosquitotransport/utils/FileUploader.kt +106 -0
- package/android/src/main/java/com/mosquitotransport/utils/UploadCallback.kt +7 -0
- package/ios/MosquitoTransport-Bridging-Header.h +1 -0
- package/ios/MosquitoTransport.h +6 -0
- package/ios/MosquitoTransport.mm +81 -0
- package/ios/{Mosquitodb.swift → MosquitoTransport.swift} +39 -42
- package/package.json +10 -2
- package/src/NativeMosquitoTransport.js +24 -0
- package/src/helpers/fs_manager.js +1 -1
- package/src/helpers/utils.js +2 -4
- package/src/helpers/variables.js +2 -1
- package/src/index.d.ts +1 -1
- package/src/index.js +15 -10
- package/src/products/auth/accessor.js +118 -94
- package/src/products/auth/index.js +7 -6
- package/src/products/database/index.js +1 -0
- package/src/products/storage/index.js +9 -23
- package/.jshintignore +0 -4
- package/.jshintrc +0 -16
- package/TODO +0 -35
- package/android/gradle.properties +0 -5
- package/android/src/main/java/com/mosquitodb/MosquitodbModule.java +0 -82
- package/android/src/main/java/com/mosquitodb/MosquitodbPackage.java +0 -28
- package/android/src/main/java/com/mosquitodb/utils/FileUploader.java +0 -101
- package/android/src/main/java/com/mosquitodb/utils/UploadCallback.java +0 -7
- package/example/.bundle/config +0 -2
- package/example/.node-version +0 -1
- package/example/.watchmanconfig +0 -1
- package/example/Gemfile +0 -6
- package/example/android/app/build.gradle +0 -170
- package/example/android/app/debug.keystore +0 -0
- package/example/android/app/proguard-rules.pro +0 -10
- package/example/android/app/src/debug/AndroidManifest.xml +0 -13
- package/example/android/app/src/debug/java/com/mosquitodbexample/ReactNativeFlipper.java +0 -75
- package/example/android/app/src/main/AndroidManifest.xml +0 -25
- package/example/android/app/src/main/java/com/mosquitodbexample/MainActivity.java +0 -35
- package/example/android/app/src/main/java/com/mosquitodbexample/MainApplication.java +0 -62
- package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -36
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/values/strings.xml +0 -3
- package/example/android/app/src/main/res/values/styles.xml +0 -9
- package/example/android/app/src/release/java/com/mosquitodbexample/ReactNativeFlipper.java +0 -20
- package/example/android/build.gradle +0 -21
- package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/example/android/gradle/wrapper/gradle-wrapper.properties +0 -5
- package/example/android/gradle.properties +0 -44
- package/example/android/gradlew +0 -234
- package/example/android/gradlew.bat +0 -89
- package/example/android/settings.gradle +0 -4
- package/example/app.json +0 -4
- package/example/babel.config.js +0 -17
- package/example/index.js +0 -5
- package/example/ios/.xcode.env +0 -11
- package/example/ios/File.swift +0 -6
- package/example/ios/MosquitodbExample/AppDelegate.h +0 -6
- package/example/ios/MosquitodbExample/AppDelegate.mm +0 -36
- package/example/ios/MosquitodbExample/Images.xcassets/AppIcon.appiconset/Contents.json +0 -53
- package/example/ios/MosquitodbExample/Images.xcassets/Contents.json +0 -6
- package/example/ios/MosquitodbExample/Info.plist +0 -55
- package/example/ios/MosquitodbExample/LaunchScreen.storyboard +0 -47
- package/example/ios/MosquitodbExample/main.m +0 -10
- package/example/ios/MosquitodbExample-Bridging-Header.h +0 -3
- package/example/ios/MosquitodbExample.xcodeproj/project.pbxproj +0 -703
- package/example/ios/MosquitodbExample.xcodeproj/xcshareddata/xcschemes/MosquitodbExample.xcscheme +0 -88
- package/example/ios/MosquitodbExample.xcworkspace/contents.xcworkspacedata +0 -10
- package/example/ios/MosquitodbExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/example/ios/MosquitodbExample.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +0 -8
- package/example/ios/MosquitodbExample.xcworkspace/xcuserdata/anthony.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/MosquitodbExample.xcworkspace/xcuserdata/anthony.xcuserdatad/WorkspaceSettings.xcsettings +0 -14
- package/example/ios/MosquitodbExampleTests/Info.plist +0 -24
- package/example/ios/MosquitodbExampleTests/MosquitodbExampleTests.m +0 -66
- package/example/ios/Podfile +0 -60
- package/example/metro.config.js +0 -40
- package/example/package.json +0 -22
- package/example/react-native.config.js +0 -10
- package/example/src/App.tsx +0 -31
- package/ios/Mosquitodb-Bridging-Header.h +0 -2
- package/ios/Mosquitodb.m +0 -22
- package/ios/Mosquitodb.xcodeproj/project.pbxproj +0 -283
- package/ios/Mosquitodb.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -4
- package/react-native-mosquitodb.podspec +0 -35
|
@@ -1,52 +1,34 @@
|
|
|
1
1
|
import React
|
|
2
2
|
|
|
3
|
-
@objc
|
|
4
|
-
|
|
3
|
+
@objc
|
|
4
|
+
public protocol NativeMosquitoTransportImplDelegate {
|
|
5
|
+
func sendEvent(name: String, body: Any)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@objc(NativeMosquitoTransportImpl)
|
|
9
|
+
public class NativeMosquitoTransportImpl: NSObject, URLSessionDataDelegate {
|
|
10
|
+
// Add property for the Objective-C bridge
|
|
11
|
+
@objc public weak var delegate: NativeMosquitoTransportImplDelegate? = nil
|
|
5
12
|
|
|
6
13
|
public override init() {
|
|
7
14
|
super.init()
|
|
8
15
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
@objc(supportedEvents)
|
|
15
|
-
override public func supportedEvents() -> [String] {
|
|
16
|
-
return [
|
|
17
|
-
"mt-uploading-progress",
|
|
18
|
-
"mt-uploading-status",
|
|
19
|
-
"mt-download-progress",
|
|
20
|
-
"mt-download-status"
|
|
21
|
-
]
|
|
16
|
+
|
|
17
|
+
private func emitNewEvent(name: String, body: Any? = nil) {
|
|
18
|
+
delegate?.sendEvent(name: name, body: body ?? [])
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
var uploadTask: [String:
|
|
25
|
-
var downloadTask: [String: MosquitodbDownloadTask] = [:]
|
|
26
|
-
|
|
27
|
-
@objc(downloadFile:)
|
|
28
|
-
func downloadFile(options: [String: Any]) -> Void {
|
|
29
|
-
let processID = options["processID"] as! String
|
|
30
|
-
downloadTask[processID] = MosquitodbDownloadTask()
|
|
31
|
-
downloadTask[processID]?.downloadFile(options: options, completion: { res in
|
|
32
|
-
let status = res![0] as? String
|
|
33
|
-
|
|
34
|
-
self.sendEvent(withName: status, body: res![1])
|
|
35
|
-
if status == "mt-download-status" {
|
|
36
|
-
self.downloadTask.removeValue(forKey: processID)
|
|
37
|
-
}
|
|
38
|
-
})
|
|
39
|
-
}
|
|
21
|
+
var uploadTask: [String: MosquitoTransportUploadTask] = [:]
|
|
40
22
|
|
|
41
23
|
@objc(uploadFile:)
|
|
42
|
-
func uploadFile(options: [String: Any]) -> Void {
|
|
24
|
+
public func uploadFile(options: [String: Any]) -> Void {
|
|
43
25
|
let processID = options["processID"] as! String
|
|
44
26
|
|
|
45
|
-
uploadTask[processID] =
|
|
27
|
+
uploadTask[processID] = MosquitoTransportUploadTask()
|
|
46
28
|
uploadTask[processID]?.uploadFile(options: options, completion: { res in
|
|
47
29
|
let status = res![0] as? String
|
|
48
30
|
|
|
49
|
-
self.
|
|
31
|
+
self.emitNewEvent(name: status!, body: res![1])
|
|
50
32
|
if status == "mt-uploading-status" {
|
|
51
33
|
self.uploadTask.removeValue(forKey: processID)
|
|
52
34
|
}
|
|
@@ -54,28 +36,43 @@ class Mosquitodb: RCTEventEmitter, URLSessionDataDelegate {
|
|
|
54
36
|
}
|
|
55
37
|
|
|
56
38
|
@objc(cancelUpload:)
|
|
57
|
-
func cancelUpload (processID: String)-> Void {
|
|
39
|
+
public func cancelUpload (processID: String)-> Void {
|
|
58
40
|
uploadTask[processID]?.cancelUpload()
|
|
59
41
|
}
|
|
60
42
|
|
|
43
|
+
var downloadTask: [String: MosquitoTransportDownloadTask] = [:]
|
|
44
|
+
|
|
45
|
+
@objc(downloadFile:)
|
|
46
|
+
public func downloadFile(options: [String: Any]) -> Void {
|
|
47
|
+
let processID = options["processID"] as! String
|
|
48
|
+
downloadTask[processID] = MosquitoTransportDownloadTask()
|
|
49
|
+
downloadTask[processID]?.downloadFile(options: options, completion: { res in
|
|
50
|
+
let status = res![0] as? String
|
|
51
|
+
|
|
52
|
+
self.emitNewEvent(name: status!, body: res![1])
|
|
53
|
+
if status == "mt-download-status" {
|
|
54
|
+
self.downloadTask.removeValue(forKey: processID)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
61
59
|
@objc(cancelDownload:)
|
|
62
|
-
func cancelDownload (processID: String)-> Void {
|
|
60
|
+
public func cancelDownload (processID: String)-> Void {
|
|
63
61
|
downloadTask[processID]?.cancelDownload()
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
@objc(pauseDownload:)
|
|
67
|
-
func pauseDownload (processID: String)-> Void {
|
|
65
|
+
public func pauseDownload (processID: String)-> Void {
|
|
68
66
|
downloadTask[processID]?.pauseDownload()
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
@objc(resumeDownload:)
|
|
72
|
-
func resumeDownload (processID: String)-> Void {
|
|
70
|
+
public func resumeDownload (processID: String)-> Void {
|
|
73
71
|
downloadTask[processID]?.resumeDownload()
|
|
74
72
|
}
|
|
75
73
|
}
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
class MosquitodbUploadTask: NSObject, URLSessionDataDelegate {
|
|
75
|
+
class MosquitoTransportUploadTask: NSObject, URLSessionDataDelegate {
|
|
79
76
|
|
|
80
77
|
var mainProcessID: String = ""
|
|
81
78
|
var mainTask : URLSessionUploadTask? = nil
|
|
@@ -177,7 +174,7 @@ class MosquitodbUploadTask: NSObject, URLSessionDataDelegate {
|
|
|
177
174
|
}
|
|
178
175
|
|
|
179
176
|
|
|
180
|
-
class
|
|
177
|
+
class MosquitoTransportDownloadTask: NSObject, URLSessionDownloadDelegate {
|
|
181
178
|
|
|
182
179
|
var mainProcessID: String = ""
|
|
183
180
|
var mainTask : URLSessionDownloadTask? = nil
|
|
@@ -208,7 +205,7 @@ class MosquitodbDownloadTask: NSObject, URLSessionDownloadDelegate {
|
|
|
208
205
|
mainOptions = options
|
|
209
206
|
trigger = completion
|
|
210
207
|
task.resume()
|
|
211
|
-
print("
|
|
208
|
+
print("MosquitoTransportDownloadTask started:\(mainProcessID)")
|
|
212
209
|
}
|
|
213
210
|
|
|
214
211
|
func cancelDownload ()-> Void {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-mosquito-transport",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.49",
|
|
4
4
|
"description": "React native javascript sdk for mosquito-transport (https://github.com/brainbehindx/mosquito-transport)",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -26,6 +26,14 @@
|
|
|
26
26
|
"url": "https://github.com/brainbehindx/react-native-mosquito-transport/issues"
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/brainbehindx/react-native-mosquito-transport#readme",
|
|
29
|
+
"codegenConfig": {
|
|
30
|
+
"name": "MosquitoTransportSpec",
|
|
31
|
+
"type": "modules",
|
|
32
|
+
"jsSrcsDir": "src",
|
|
33
|
+
"android": {
|
|
34
|
+
"javaPackageName": "com.mosquitotransport"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
29
37
|
"dependencies": {
|
|
30
38
|
"@turf/turf": "^7.2.0",
|
|
31
39
|
"buffer": "^6.0.3",
|
|
@@ -43,4 +51,4 @@
|
|
|
43
51
|
"react-native-get-random-values": "*",
|
|
44
52
|
"react-native-sha256": "*"
|
|
45
53
|
}
|
|
46
|
-
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type { TurboModule } from 'react-native';
|
|
3
|
+
import { type UnsafeObject } from 'react-native/Libraries/Types/CodegenTypes';
|
|
4
|
+
import { TurboModuleRegistry } from 'react-native';
|
|
5
|
+
|
|
6
|
+
export interface Spec extends TurboModule {
|
|
7
|
+
// storage
|
|
8
|
+
uploadFile(option: UnsafeObject): void;
|
|
9
|
+
cancelUpload(process_id: string): void;
|
|
10
|
+
downloadFile(option: UnsafeObject): void;
|
|
11
|
+
cancelDownload(process_id: string): void;
|
|
12
|
+
pauseDownload(process_id: string): void;
|
|
13
|
+
resumeDownload(process_id: string): void;
|
|
14
|
+
|
|
15
|
+
// utils
|
|
16
|
+
getSystemUptime(): Promise<number>;
|
|
17
|
+
|
|
18
|
+
// event listeners
|
|
19
|
+
// readonly onMessage?: EventEmitter<{ message: string }>;
|
|
20
|
+
addListener(eventName: string): void;
|
|
21
|
+
removeListeners(count: number): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default TurboModuleRegistry.getEnforcing<Spec>('MosquitoTransport');
|
|
@@ -4,7 +4,7 @@ import { Platform } from "react-native";
|
|
|
4
4
|
import { Dirs, FileSystem } from "react-native-file-access";
|
|
5
5
|
import { Buffer } from "buffer";
|
|
6
6
|
|
|
7
|
-
const PARENT_FOLDER = `${Platform.OS === 'android' ? Dirs.DocumentDir.split('/').slice(0, -1).join('/') : Dirs.
|
|
7
|
+
const PARENT_FOLDER = `${Platform.OS === 'android' ? Dirs.DocumentDir.split('/').slice(0, -1).join('/') : Dirs.LibraryDir}/mosquito_base`;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* this method linearize read/write for individual access_id on the file system ensuring consistency across concurrent operations
|
package/src/helpers/utils.js
CHANGED
|
@@ -134,21 +134,19 @@ export const releaseCacheStore = async (builder) => {
|
|
|
134
134
|
Scoped.AuthJWTToken[key] = value?.token;
|
|
135
135
|
});
|
|
136
136
|
Scoped.IsStoreReady = true;
|
|
137
|
-
StoreReadyListener.dispatchPersist('_',
|
|
137
|
+
StoreReadyListener.dispatchPersist('_', true);
|
|
138
138
|
setTimeout(() => {
|
|
139
139
|
if (tobePurged.length) updateCacheStore(tobePurged);
|
|
140
140
|
}, 0);
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
export const getPrefferTime = () => Date.now() + (Scoped.serverTimeOffset || 0);
|
|
144
|
-
|
|
145
143
|
export const awaitStore = () => new Promise(resolve => {
|
|
146
144
|
if (Scoped.IsStoreReady) {
|
|
147
145
|
resolve();
|
|
148
146
|
return;
|
|
149
147
|
}
|
|
150
148
|
const l = StoreReadyListener.listenToPersist('_', t => {
|
|
151
|
-
if (t
|
|
149
|
+
if (t) {
|
|
152
150
|
resolve();
|
|
153
151
|
l();
|
|
154
152
|
}
|
package/src/helpers/variables.js
CHANGED
|
@@ -10,7 +10,8 @@ export const Scoped = {
|
|
|
10
10
|
AuthJWTToken: {},
|
|
11
11
|
IsStoreReady: false,
|
|
12
12
|
TokenRefreshTimer: {},
|
|
13
|
-
|
|
13
|
+
TokenRefreshProcess: {},
|
|
14
|
+
TokenTimestamping: {},
|
|
14
15
|
StorageProcessID: 0,
|
|
15
16
|
InitiatedForcedToken: {},
|
|
16
17
|
PendingFetchCollective: {},
|
package/src/index.d.ts
CHANGED
|
@@ -347,7 +347,7 @@ interface GetConfig {
|
|
|
347
347
|
retrieval?: Retrieval;
|
|
348
348
|
/**
|
|
349
349
|
* - 0: returns data that may have been internally updated locally with updateOne, updateMany, mergeOne, deleteOne, deleteMany, putOne, replaceOne
|
|
350
|
-
* - 1: returns exact data which was cached in the last query process
|
|
350
|
+
* - 1: returns exact remote data which was cached in the last query process
|
|
351
351
|
*
|
|
352
352
|
* @defaults - 0
|
|
353
353
|
*
|
package/src/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { Buffer } from 'buffer';
|
|
|
16
16
|
import MTAuth, { purgePendingToken } from './products/auth';
|
|
17
17
|
import { BSON } from "./vendor/bson";
|
|
18
18
|
import { basicClone } from './helpers/basic_clone';
|
|
19
|
+
import { AppState } from "react-native";
|
|
19
20
|
|
|
20
21
|
const {
|
|
21
22
|
_listenCollection,
|
|
@@ -59,11 +60,10 @@ class RNMT {
|
|
|
59
60
|
|
|
60
61
|
if (!Scoped.InitializedProject[projectUrl]) {
|
|
61
62
|
Scoped.InitializedProject[projectUrl] = basicClone(this.config);
|
|
62
|
-
Scoped.LastTokenRefreshRef[projectUrl] = 0;
|
|
63
63
|
triggerAuthToken(projectUrl);
|
|
64
|
-
initTokenRefresher({
|
|
64
|
+
initTokenRefresher({ config: this.config, forceRefresh: true });
|
|
65
65
|
|
|
66
|
-
let isConnected, recentToken;
|
|
66
|
+
let isConnected, recentToken, isVirtualMachineFocused = true;
|
|
67
67
|
|
|
68
68
|
const socket = io(`${this.config.wsPrefix}://${this.config.baseUrl}`, {
|
|
69
69
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
@@ -86,9 +86,9 @@ class RNMT {
|
|
|
86
86
|
};
|
|
87
87
|
const onDisconnect = () => {
|
|
88
88
|
++connectionIte;
|
|
89
|
-
isConnected = false;
|
|
90
|
-
Scoped.IS_CONNECTED[projectUrl] =
|
|
91
|
-
ServerReachableListener.dispatchPersist(projectUrl,
|
|
89
|
+
isConnected = isVirtualMachineFocused ? false : null;
|
|
90
|
+
Scoped.IS_CONNECTED[projectUrl] = isConnected;
|
|
91
|
+
ServerReachableListener.dispatchPersist(projectUrl, isConnected);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
const manualCheckConnection = () => {
|
|
@@ -112,6 +112,11 @@ class RNMT {
|
|
|
112
112
|
manualCheckConnection();
|
|
113
113
|
});
|
|
114
114
|
|
|
115
|
+
AppState.addEventListener('change', (s) => {
|
|
116
|
+
isVirtualMachineFocused = s === 'active';
|
|
117
|
+
manualCheckConnection();
|
|
118
|
+
});
|
|
119
|
+
|
|
115
120
|
const updateMountedToken = () => {
|
|
116
121
|
socket.emit('_update_mounted_user', recentToken || null);
|
|
117
122
|
};
|
|
@@ -308,7 +313,7 @@ class RNMT {
|
|
|
308
313
|
tokenListener = listenTokenReady(status => {
|
|
309
314
|
if (lastTokenStatus === (status || false)) return;
|
|
310
315
|
|
|
311
|
-
if (status
|
|
316
|
+
if (status) {
|
|
312
317
|
init();
|
|
313
318
|
} else {
|
|
314
319
|
socket?.close?.();
|
|
@@ -448,7 +453,7 @@ const ConfigValidator = {
|
|
|
448
453
|
if (v.endsWith('/')) throw '"projectUrl" must not end with a trailing slash "/"';
|
|
449
454
|
},
|
|
450
455
|
disableCache: (v) => {
|
|
451
|
-
if (typeof v !== 'boolean')
|
|
456
|
+
if (typeof v !== 'boolean' && v !== undefined)
|
|
452
457
|
throw `Invalid value supplied to disableCache, value must be a boolean`;
|
|
453
458
|
},
|
|
454
459
|
maxRetries: (v) => {
|
|
@@ -456,7 +461,7 @@ const ConfigValidator = {
|
|
|
456
461
|
throw `Invalid value supplied to maxRetries, value must be positive integer greater than zero`;
|
|
457
462
|
},
|
|
458
463
|
enableE2E_Encryption: (v) => {
|
|
459
|
-
if (typeof v !== 'boolean')
|
|
464
|
+
if (typeof v !== 'boolean' && v !== undefined)
|
|
460
465
|
throw `Invalid value supplied to enableE2E_Encryption, value must be a boolean`;
|
|
461
466
|
},
|
|
462
467
|
castBSON: v => {
|
|
@@ -468,7 +473,7 @@ const ConfigValidator = {
|
|
|
468
473
|
throw `Expected "borrowToken" to be valid https or http link but got "${v}"`;
|
|
469
474
|
},
|
|
470
475
|
serverE2E_PublicKey: (v) => {
|
|
471
|
-
if (typeof v !== 'string' || !v.trim())
|
|
476
|
+
if (v !== undefined && (typeof v !== 'string' || !v.trim()))
|
|
472
477
|
throw `Invalid value supplied to serverETE_PublicKey, value must be a non-empty string`;
|
|
473
478
|
},
|
|
474
479
|
extraHeaders: v => {
|
|
@@ -2,11 +2,12 @@ import { doSignOut, revokeAuthIntance } from "./index.js";
|
|
|
2
2
|
import EngineApi from "../../helpers/engine_api";
|
|
3
3
|
import { AuthTokenListener, TokenRefreshListener } from "../../helpers/listeners";
|
|
4
4
|
import { decodeBinary, deserializeE2E, listenReachableServer } from "../../helpers/peripherals";
|
|
5
|
-
import { awaitStore, buildFetchInterface, buildFetchResult,
|
|
5
|
+
import { awaitStore, buildFetchInterface, buildFetchResult, updateCacheStore } from "../../helpers/utils";
|
|
6
6
|
import { CacheStore, Scoped } from "../../helpers/variables";
|
|
7
7
|
import { simplifyError } from "simplify-error";
|
|
8
8
|
import { Validator } from "guard-object";
|
|
9
9
|
import { basicClone } from "../../helpers/basic_clone";
|
|
10
|
+
import NativeMosquitoTransport from "../../NativeMosquitoTransport.js";
|
|
10
11
|
|
|
11
12
|
export const listenToken = (callback, projectUrl) =>
|
|
12
13
|
AuthTokenListener.listenToPersist(projectUrl, (t, n) => {
|
|
@@ -17,16 +18,16 @@ export const listenToken = (callback, projectUrl) =>
|
|
|
17
18
|
export const injectFreshToken = async (config, { token, refreshToken }) => {
|
|
18
19
|
const { projectUrl } = config;
|
|
19
20
|
|
|
20
|
-
await awaitStore();
|
|
21
21
|
CacheStore.AuthStore[projectUrl] = { token, refreshToken };
|
|
22
22
|
Scoped.AuthJWTToken[projectUrl] = token;
|
|
23
23
|
const isEmulated = projectUrl in CacheStore.EmulatedAuth;
|
|
24
24
|
if (isEmulated) delete CacheStore.EmulatedAuth[projectUrl];
|
|
25
|
+
await updateTokenTimestamp(projectUrl, token);
|
|
25
26
|
|
|
26
27
|
updateCacheStore(['AuthStore', isEmulated ? 'EmulatedAuth' : ''].filter(v => v));
|
|
27
28
|
|
|
28
29
|
triggerAuthToken(projectUrl);
|
|
29
|
-
initTokenRefresher(config);
|
|
30
|
+
initTokenRefresher({ config });
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
export const injectEmulatedAuth = async (config, emulatedURL) => {
|
|
@@ -49,7 +50,7 @@ export const injectEmulatedAuth = async (config, emulatedURL) => {
|
|
|
49
50
|
|
|
50
51
|
updateCacheStore(['AuthStore', 'EmulatedAuth']);
|
|
51
52
|
triggerAuthToken(projectUrl);
|
|
52
|
-
initTokenRefresher(config);
|
|
53
|
+
initTokenRefresher({ config });
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
export const parseToken = (token) => JSON.parse(decodeBinary(token.split('.')[1]));
|
|
@@ -59,28 +60,35 @@ export const triggerAuthToken = async (projectUrl, isInit) => {
|
|
|
59
60
|
AuthTokenListener.dispatchPersist(projectUrl, CacheStore.AuthStore[projectUrl]?.token || null, isInit);
|
|
60
61
|
};
|
|
61
62
|
|
|
62
|
-
export const awaitRefreshToken = (projectUrl) =>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
export const awaitRefreshToken = (projectUrl) =>
|
|
64
|
+
new Promise(async resolve => {
|
|
65
|
+
try {
|
|
66
|
+
if (await initTokenRefresher({ justCheck: true, config: Scoped.InitializedProject[projectUrl] })) {
|
|
67
|
+
resolve();
|
|
68
|
+
} else throw null;
|
|
69
|
+
} catch (_) {
|
|
70
|
+
const l = TokenRefreshListener.listenToPersist(projectUrl, v => {
|
|
71
|
+
if (v) {
|
|
72
|
+
l();
|
|
73
|
+
resolve();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
67
76
|
}
|
|
68
77
|
});
|
|
69
|
-
});
|
|
70
78
|
|
|
71
79
|
export const listenTokenReady = (callback, projectUrl) => TokenRefreshListener.listenToPersist(projectUrl, callback);
|
|
72
80
|
|
|
73
|
-
export const initTokenRefresher = async (config, forceRefresh) => {
|
|
81
|
+
export const initTokenRefresher = async ({ config, forceRefresh, justCheck }) => {
|
|
74
82
|
const { projectUrl, maxRetries } = config;
|
|
75
83
|
if (!Scoped.IsStoreReady) await awaitStore();
|
|
76
84
|
const { token } = CacheStore.AuthStore[projectUrl] || {};
|
|
77
85
|
const emulatedURL = CacheStore.EmulatedAuth[projectUrl];
|
|
78
|
-
const tokenInfo = token && parseToken(token);
|
|
79
86
|
|
|
80
|
-
clearInterval(Scoped.TokenRefreshTimer[projectUrl]);
|
|
87
|
+
if (!justCheck) clearInterval(Scoped.TokenRefreshTimer[projectUrl]);
|
|
81
88
|
if (emulatedURL) return;
|
|
82
89
|
|
|
83
90
|
const notifyAuthReady = (value) => {
|
|
91
|
+
if (justCheck) return;
|
|
84
92
|
TokenRefreshListener.dispatchPersist(projectUrl, value);
|
|
85
93
|
getEmulatedLinks(projectUrl).forEach(v => {
|
|
86
94
|
TokenRefreshListener.dispatchPersist(v, value);
|
|
@@ -88,104 +96,120 @@ export const initTokenRefresher = async (config, forceRefresh) => {
|
|
|
88
96
|
}
|
|
89
97
|
|
|
90
98
|
if (token) {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
const rizz = () => {
|
|
100
|
+
const runningProcess = Scoped.TokenRefreshProcess[projectUrl];
|
|
101
|
+
if (runningProcess) return runningProcess;
|
|
102
|
+
|
|
103
|
+
Scoped.TokenRefreshProcess[projectUrl] =
|
|
104
|
+
refreshToken(config, maxRetries, forceRefresh);
|
|
105
|
+
|
|
106
|
+
Scoped.TokenRefreshProcess[projectUrl].finally(() => {
|
|
107
|
+
delete Scoped.TokenRefreshProcess[projectUrl];
|
|
108
|
+
});
|
|
109
|
+
}
|
|
94
110
|
|
|
95
|
-
if (
|
|
111
|
+
if (await hasTokenExpire(projectUrl) || forceRefresh) {
|
|
96
112
|
notifyAuthReady();
|
|
97
113
|
return rizz();
|
|
98
114
|
} else {
|
|
99
|
-
notifyAuthReady(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
115
|
+
notifyAuthReady(true);
|
|
116
|
+
if (justCheck) {
|
|
117
|
+
return true;
|
|
118
|
+
} else {
|
|
119
|
+
let lastIte = 0;
|
|
103
120
|
clearInterval(Scoped.TokenRefreshTimer[projectUrl]);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
121
|
+
Scoped.TokenRefreshTimer[projectUrl] = setInterval(async () => {
|
|
122
|
+
const iteRef = ++lastIte;
|
|
123
|
+
if (iteRef !== lastIte || !(await hasTokenExpire(projectUrl))) return;
|
|
124
|
+
clearInterval(Scoped.TokenRefreshTimer[projectUrl]);
|
|
125
|
+
notifyAuthReady();
|
|
126
|
+
rizz();
|
|
127
|
+
}, 7000);
|
|
128
|
+
}
|
|
107
129
|
}
|
|
108
130
|
} else {
|
|
109
|
-
notifyAuthReady(
|
|
110
|
-
if (
|
|
111
|
-
return simplifyError('no_token_yet', 'No token is available to initiate a refresh').simpleError;
|
|
112
|
-
}
|
|
131
|
+
notifyAuthReady(true);
|
|
132
|
+
if (justCheck) return true;
|
|
113
133
|
}
|
|
114
134
|
};
|
|
115
135
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
const hasTokenExpire = async (projectUrl) => {
|
|
137
|
+
const timestamp = Scoped.TokenTimestamping[projectUrl];
|
|
138
|
+
if (!timestamp) return true;
|
|
139
|
+
const uptime = await NativeMosquitoTransport.getSystemUptime();
|
|
119
140
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const lostProcess = simplifyError('process_lost', 'The token refresh process has been lost and replaced with another one');
|
|
141
|
+
return timestamp.ttl <= (uptime - timestamp.uptime);
|
|
142
|
+
}
|
|
123
143
|
|
|
124
|
-
|
|
125
|
-
|
|
144
|
+
const updateTokenTimestamp = async (projectUrl, token) => {
|
|
145
|
+
const { exp, iat } = parseToken(token);
|
|
146
|
+
Scoped.TokenTimestamping[projectUrl] = {
|
|
147
|
+
ttl: ((exp * 1000) - (iat * 1000)) - 60_000,
|
|
148
|
+
uptime: await NativeMosquitoTransport.getSystemUptime()
|
|
149
|
+
};
|
|
150
|
+
}
|
|
126
151
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
serverE2E_PublicKey,
|
|
131
|
-
extraHeaders
|
|
132
|
-
});
|
|
152
|
+
export const getEmulatedLinks = (projectUrl) => Object.entries(CacheStore.EmulatedAuth)
|
|
153
|
+
.filter(([_, v]) => v === projectUrl)
|
|
154
|
+
.map(v => v[0]);
|
|
133
155
|
|
|
134
|
-
|
|
156
|
+
const refreshToken = (builder, remainRetries = 1, isForceRefresh) =>
|
|
157
|
+
new Promise(async (resolve, reject) => {
|
|
158
|
+
const { projectUrl, serverE2E_PublicKey, uglify, extraHeaders } = builder;
|
|
159
|
+
const lostProcess = simplifyError('process_lost', 'The token refresh process has been lost and replaced with another one');
|
|
135
160
|
|
|
136
161
|
try {
|
|
137
|
-
|
|
138
|
-
} finally {
|
|
139
|
-
if (processRef !== Scoped.LastTokenRefreshRef[projectUrl]) {
|
|
140
|
-
reject(lostProcess.simpleError);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
146
|
-
|
|
147
|
-
if (CacheStore.AuthStore[projectUrl]) {
|
|
148
|
-
CacheStore.AuthStore[projectUrl].token = f.result.token;
|
|
149
|
-
Scoped.AuthJWTToken[projectUrl] = f.result.token;
|
|
150
|
-
|
|
151
|
-
resolve(f.result.token);
|
|
152
|
-
const isInit = !Scoped.InitiatedForcedToken[projectUrl] && isForceRefresh;
|
|
153
|
-
|
|
154
|
-
triggerAuthToken(projectUrl, isInit);
|
|
155
|
-
if (isForceRefresh) Scoped.InitiatedForcedToken[projectUrl] = true;
|
|
162
|
+
const { token, refreshToken: r_token } = CacheStore.AuthStore[projectUrl];
|
|
156
163
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (isForceRefresh) Scoped.InitiatedForcedToken[v] = true;
|
|
164
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
165
|
+
body: { token, r_token },
|
|
166
|
+
uglify,
|
|
167
|
+
serverE2E_PublicKey,
|
|
168
|
+
extraHeaders
|
|
163
169
|
});
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
|
|
171
|
+
const data = await buildFetchResult(await fetch(EngineApi._refreshAuthToken(projectUrl, uglify), reqBuilder), uglify);
|
|
172
|
+
|
|
173
|
+
const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
174
|
+
|
|
175
|
+
if (CacheStore.AuthStore[projectUrl]) {
|
|
176
|
+
CacheStore.AuthStore[projectUrl].token = f.result.token;
|
|
177
|
+
Scoped.AuthJWTToken[projectUrl] = f.result.token;
|
|
178
|
+
await updateTokenTimestamp(projectUrl, f.result.token);
|
|
179
|
+
|
|
180
|
+
resolve(f.result.token);
|
|
181
|
+
const isInit = !Scoped.InitiatedForcedToken[projectUrl] && isForceRefresh;
|
|
182
|
+
|
|
183
|
+
triggerAuthToken(projectUrl, isInit);
|
|
184
|
+
if (isForceRefresh) Scoped.InitiatedForcedToken[projectUrl] = true;
|
|
185
|
+
|
|
186
|
+
getEmulatedLinks(projectUrl).forEach(v => {
|
|
187
|
+
CacheStore.AuthStore[v] = basicClone(CacheStore.AuthStore[projectUrl]);
|
|
188
|
+
Scoped.AuthJWTToken[v] = f.result.token;
|
|
189
|
+
|
|
190
|
+
triggerAuthToken(v, isInit);
|
|
191
|
+
if (isForceRefresh) Scoped.InitiatedForcedToken[v] = true;
|
|
192
|
+
});
|
|
193
|
+
updateCacheStore(['AuthStore']);
|
|
194
|
+
initTokenRefresher({ config: builder });
|
|
195
|
+
} else reject(lostProcess.simpleError);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
if (e.simpleError) {
|
|
198
|
+
console.error(`refreshToken error: ${e.simpleError?.message}`);
|
|
199
|
+
doSignOut({ ...builder });
|
|
200
|
+
reject(e.simpleError);
|
|
201
|
+
} else if (remainRetries <= 0) {
|
|
202
|
+
reject(
|
|
176
203
|
simplifyError('retry_limit_reached', 'The retry limit has been reach and execution prematurely stopped').simpleError
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
}, projectUrl);
|
|
204
|
+
);
|
|
205
|
+
console.error(`refreshToken retry limit exceeded`);
|
|
206
|
+
} else {
|
|
207
|
+
const l = listenReachableServer(c => {
|
|
208
|
+
if (c) {
|
|
209
|
+
l();
|
|
210
|
+
refreshToken(builder, remainRetries - 1, isForceRefresh).then(resolve, reject);
|
|
211
|
+
}
|
|
212
|
+
}, projectUrl);
|
|
213
|
+
}
|
|
189
214
|
}
|
|
190
|
-
}
|
|
191
|
-
});
|
|
215
|
+
});
|