@dora-cell/sdk 1.0.2 → 3.0.0
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/dist/index.d.mts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +140 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +140 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -23011,7 +23011,6 @@ var ApiTokenAuthProvider = class {
|
|
|
23011
23011
|
}
|
|
23012
23012
|
const baseUrl = apiBaseUrl?.replace(/\/$/, "") || "https://api.cell.usedora.com/api";
|
|
23013
23013
|
try {
|
|
23014
|
-
console.log("SDK: Verifying keys at:", `${baseUrl}/sdk/v1/auth/session`);
|
|
23015
23014
|
const authResponse = await fetch(`${baseUrl}/sdk/v1/auth/session`, {
|
|
23016
23015
|
method: "POST",
|
|
23017
23016
|
headers: {
|
|
@@ -23033,7 +23032,29 @@ var ApiTokenAuthProvider = class {
|
|
|
23033
23032
|
if (!this.sessionToken) {
|
|
23034
23033
|
throw new AuthenticationError("No session token returned after key verification");
|
|
23035
23034
|
}
|
|
23036
|
-
|
|
23035
|
+
const origin = typeof window !== "undefined" ? window.location.origin : "";
|
|
23036
|
+
const validateResponse = await fetch(`${baseUrl}/sdk/v1/auth/validate`, {
|
|
23037
|
+
method: "GET",
|
|
23038
|
+
headers: {
|
|
23039
|
+
"Authorization": `Bearer ${this.sessionToken}`,
|
|
23040
|
+
"x-dora-public-key": this.publicKey,
|
|
23041
|
+
"Origin": origin,
|
|
23042
|
+
"Accept": "application/json"
|
|
23043
|
+
}
|
|
23044
|
+
});
|
|
23045
|
+
if (!validateResponse.ok) {
|
|
23046
|
+
throw new AuthenticationError(
|
|
23047
|
+
`Secondary validation failed: ${validateResponse.status}`,
|
|
23048
|
+
{ status: validateResponse.status }
|
|
23049
|
+
);
|
|
23050
|
+
}
|
|
23051
|
+
const validateData = await validateResponse.json();
|
|
23052
|
+
console.log(`Dora Cell SDK: Authenticated successfully`);
|
|
23053
|
+
if (validateData.features && !validateData.features.includes("voice")) {
|
|
23054
|
+
console.warn('Dora Cell SDK: App token does not have the "voice" feature enabled.');
|
|
23055
|
+
}
|
|
23056
|
+
const actualResponseData = authData.data && typeof authData.data === "object" ? authData.data : authData;
|
|
23057
|
+
this.credentials = this.parseCredentials(actualResponseData);
|
|
23037
23058
|
return this.credentials;
|
|
23038
23059
|
} catch (error) {
|
|
23039
23060
|
if (error instanceof AuthenticationError) {
|
|
@@ -23071,7 +23092,8 @@ var ApiTokenAuthProvider = class {
|
|
|
23071
23092
|
if (!sipUri && extensions.length > 0) {
|
|
23072
23093
|
const ext = extensions[0].extension;
|
|
23073
23094
|
sipUri = `sip:${ext}@${sipDomain}`;
|
|
23074
|
-
|
|
23095
|
+
} else if (!sipUri) {
|
|
23096
|
+
sipUri = "";
|
|
23075
23097
|
}
|
|
23076
23098
|
return {
|
|
23077
23099
|
wsUrl,
|
|
@@ -23172,6 +23194,7 @@ var CallSession = class {
|
|
|
23172
23194
|
// JsSIP RTCSession
|
|
23173
23195
|
this._isMuted = false;
|
|
23174
23196
|
this.remoteStreamValue = null;
|
|
23197
|
+
this.lastKnownSSRCs = /* @__PURE__ */ new Set();
|
|
23175
23198
|
this.id = this.generateCallId();
|
|
23176
23199
|
this.session = session;
|
|
23177
23200
|
this.direction = direction;
|
|
@@ -23189,6 +23212,9 @@ var CallSession = class {
|
|
|
23189
23212
|
if (code === 180 || code === 183) {
|
|
23190
23213
|
this.status = "ringing";
|
|
23191
23214
|
this.events.emit("call:ringing", this);
|
|
23215
|
+
if (this.session.connection) {
|
|
23216
|
+
setTimeout(() => this.reattachFromReceivers(this.session.connection), 200);
|
|
23217
|
+
}
|
|
23192
23218
|
}
|
|
23193
23219
|
});
|
|
23194
23220
|
this.session.on("confirmed", () => {
|
|
@@ -23196,28 +23222,79 @@ var CallSession = class {
|
|
|
23196
23222
|
this.startTime = Date.now();
|
|
23197
23223
|
this.startDurationTimer();
|
|
23198
23224
|
this.events.emit("call:connected", this);
|
|
23225
|
+
if (this.session.connection) {
|
|
23226
|
+
this.reattachFromReceivers(this.session.connection);
|
|
23227
|
+
this.startSSRCWatch(this.session.connection);
|
|
23228
|
+
}
|
|
23199
23229
|
});
|
|
23200
23230
|
this.session.on("peerconnection", (evt) => {
|
|
23201
|
-
evt.peerconnection
|
|
23231
|
+
const pc = evt.peerconnection;
|
|
23232
|
+
pc.addEventListener("track", (event) => {
|
|
23233
|
+
setTimeout(() => this.reattachFromReceivers(pc), 150);
|
|
23202
23234
|
if (event.streams && event.streams[0]) {
|
|
23203
23235
|
this.remoteStreamValue = event.streams[0];
|
|
23236
|
+
this.events.emit("call:stream", this, event.streams[0]);
|
|
23237
|
+
}
|
|
23238
|
+
});
|
|
23239
|
+
pc.oniceconnectionstatechange = () => {
|
|
23240
|
+
if (pc.iceConnectionState === "connected" || pc.iceConnectionState === "completed") {
|
|
23241
|
+
this.reattachFromReceivers(pc);
|
|
23204
23242
|
}
|
|
23205
23243
|
};
|
|
23206
23244
|
});
|
|
23207
23245
|
this.session.on("ended", (evt) => {
|
|
23246
|
+
console.log(`Dora Cell SDK: Call ended (${evt?.cause || "Normal"})`);
|
|
23208
23247
|
this.handleCallEnd(evt?.cause);
|
|
23209
23248
|
});
|
|
23210
23249
|
this.session.on("failed", (evt) => {
|
|
23250
|
+
console.warn(`Dora Cell SDK: Call failed (${evt?.cause || "Internal Error"})`);
|
|
23211
23251
|
this.handleCallEnd(evt?.cause || "Call failed");
|
|
23212
23252
|
});
|
|
23213
23253
|
this.session.on("rejected", (evt) => {
|
|
23254
|
+
console.warn(`SDK: Call rejected. Cause: ${evt?.cause || "Rejected"}`);
|
|
23214
23255
|
this.handleCallEnd(evt?.cause || "Call rejected");
|
|
23215
23256
|
});
|
|
23216
23257
|
}
|
|
23258
|
+
startSSRCWatch(pc) {
|
|
23259
|
+
this.stopSSRCWatch();
|
|
23260
|
+
this.lastKnownSSRCs.clear();
|
|
23261
|
+
this.ssrcWatchInterval = window.setInterval(async () => {
|
|
23262
|
+
try {
|
|
23263
|
+
if (!pc) return;
|
|
23264
|
+
const stats = await pc.getStats();
|
|
23265
|
+
stats.forEach((report) => {
|
|
23266
|
+
if (report.type === "inbound-rtp" && report.kind === "audio") {
|
|
23267
|
+
const ssrc = report.ssrc;
|
|
23268
|
+
if (ssrc && !this.lastKnownSSRCs.has(ssrc)) {
|
|
23269
|
+
this.lastKnownSSRCs.add(ssrc);
|
|
23270
|
+
setTimeout(() => this.reattachFromReceivers(pc), 200);
|
|
23271
|
+
}
|
|
23272
|
+
}
|
|
23273
|
+
});
|
|
23274
|
+
} catch (e) {
|
|
23275
|
+
}
|
|
23276
|
+
}, 1e3);
|
|
23277
|
+
}
|
|
23278
|
+
stopSSRCWatch() {
|
|
23279
|
+
if (this.ssrcWatchInterval) {
|
|
23280
|
+
clearInterval(this.ssrcWatchInterval);
|
|
23281
|
+
this.ssrcWatchInterval = void 0;
|
|
23282
|
+
}
|
|
23283
|
+
}
|
|
23284
|
+
reattachFromReceivers(pc) {
|
|
23285
|
+
if (!pc) return;
|
|
23286
|
+
const liveAudioTracks = pc.getReceivers().map((r) => r.track).filter((t) => t && t.kind === "audio" && t.readyState === "live");
|
|
23287
|
+
if (liveAudioTracks.length > 0) {
|
|
23288
|
+
const newStream = new MediaStream(liveAudioTracks);
|
|
23289
|
+
this.remoteStreamValue = newStream;
|
|
23290
|
+
this.events.emit("call:stream", this, newStream);
|
|
23291
|
+
}
|
|
23292
|
+
}
|
|
23217
23293
|
handleCallEnd(reason) {
|
|
23218
23294
|
this.status = "ended";
|
|
23219
23295
|
this.endTime = Date.now();
|
|
23220
23296
|
this.stopDurationTimer();
|
|
23297
|
+
this.stopSSRCWatch();
|
|
23221
23298
|
this.remoteStreamValue = null;
|
|
23222
23299
|
this._isMuted = false;
|
|
23223
23300
|
this.events.emit("call:ended", this, reason);
|
|
@@ -23298,6 +23375,7 @@ var CallManager = class {
|
|
|
23298
23375
|
const extension = options?.extension || this.getDefaultExtension();
|
|
23299
23376
|
const sipDomain = this.extractDomain(this.credentials.sipUri);
|
|
23300
23377
|
const sipTarget = formatPhoneToSIP(targetNumber, sipDomain);
|
|
23378
|
+
console.log(`Dora Cell SDK: Initiating call to ${targetNumber}...`);
|
|
23301
23379
|
const session = this.ua.call(sipTarget, {
|
|
23302
23380
|
mediaConstraints: options?.mediaConstraints || { audio: true },
|
|
23303
23381
|
pcConfig: this.callConfig.pcConfig
|
|
@@ -23324,12 +23402,13 @@ var CallManager = class {
|
|
|
23324
23402
|
* Answer the pending incoming call.
|
|
23325
23403
|
* Uses the stored pendingSession from handleIncomingCall.
|
|
23326
23404
|
*/
|
|
23327
|
-
answerCurrentCall() {
|
|
23405
|
+
async answerCurrentCall() {
|
|
23328
23406
|
const session = this.pendingSession;
|
|
23329
23407
|
if (!session) {
|
|
23330
23408
|
throw new CallError("No pending incoming call to answer");
|
|
23331
23409
|
}
|
|
23332
23410
|
try {
|
|
23411
|
+
await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
23333
23412
|
session.answer({
|
|
23334
23413
|
mediaConstraints: { audio: true },
|
|
23335
23414
|
pcConfig: this.callConfig.pcConfig
|
|
@@ -23374,10 +23453,13 @@ var CallManager = class {
|
|
|
23374
23453
|
this.pendingSession = null;
|
|
23375
23454
|
}
|
|
23376
23455
|
getDefaultExtension() {
|
|
23456
|
+
if (this.credentials.sipUri) {
|
|
23457
|
+
return this.extractExtension(this.credentials.sipUri);
|
|
23458
|
+
}
|
|
23377
23459
|
if (this.credentials.extensions && this.credentials.extensions.length > 0) {
|
|
23378
23460
|
return this.credentials.extensions[0].extension;
|
|
23379
23461
|
}
|
|
23380
|
-
return
|
|
23462
|
+
return "unknown";
|
|
23381
23463
|
}
|
|
23382
23464
|
extractExtension(sipUri) {
|
|
23383
23465
|
const match = sipUri.match(/sip:([^@]+)@/);
|
|
@@ -23461,6 +23543,7 @@ var DoraCell = class {
|
|
|
23461
23543
|
this.connectionStatus = "disconnected";
|
|
23462
23544
|
this.retryCount = 0;
|
|
23463
23545
|
this.maxRetries = 3;
|
|
23546
|
+
this.userId = null;
|
|
23464
23547
|
this.config = {
|
|
23465
23548
|
autoSelectExtension: true,
|
|
23466
23549
|
debug: false,
|
|
@@ -23485,17 +23568,25 @@ var DoraCell = class {
|
|
|
23485
23568
|
);
|
|
23486
23569
|
if (this.authProvider instanceof ApiTokenAuthProvider) {
|
|
23487
23570
|
const token = this.authProvider.getSessionToken();
|
|
23488
|
-
if (token)
|
|
23571
|
+
if (token) {
|
|
23572
|
+
this.apiClient.setSessionToken(token);
|
|
23573
|
+
}
|
|
23574
|
+
}
|
|
23575
|
+
await this.getWallet().catch(() => {
|
|
23576
|
+
});
|
|
23577
|
+
if (!this.credentials?.extensions || this.credentials.extensions.length === 0) {
|
|
23578
|
+
await this.fetchExtensions();
|
|
23489
23579
|
}
|
|
23490
23580
|
if (this.config.autoSelectExtension && this.credentials?.extensions && this.credentials.extensions.length > 0) {
|
|
23491
23581
|
const primary = this.credentials.extensions.find((e) => e.isPrimary) || this.credentials.extensions[0];
|
|
23492
|
-
const domain = this.credentials.sipDomain || "
|
|
23582
|
+
const domain = this.credentials.sipDomain || "64.227.10.164";
|
|
23493
23583
|
this.credentials.sipUri = `sip:${primary.extension}@${domain}`;
|
|
23494
|
-
console.log(`SDK: Auto-selected extension ${primary.extension}`);
|
|
23495
23584
|
}
|
|
23496
|
-
|
|
23497
|
-
|
|
23498
|
-
|
|
23585
|
+
if (this.credentials?.sipUri) {
|
|
23586
|
+
await this.initializeUserAgent();
|
|
23587
|
+
this.initializeCallManager();
|
|
23588
|
+
await this.waitForRegistration();
|
|
23589
|
+
}
|
|
23499
23590
|
} catch (error) {
|
|
23500
23591
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
23501
23592
|
this.emitError(new ConnectionError(`Initialization failed: ${errorMessage}`));
|
|
@@ -23529,6 +23620,16 @@ var DoraCell = class {
|
|
|
23529
23620
|
if (!this.credentials) {
|
|
23530
23621
|
throw new ConnectionError("No credentials available");
|
|
23531
23622
|
}
|
|
23623
|
+
this.connectionStatus = "connecting";
|
|
23624
|
+
this.emitConnectionStatus();
|
|
23625
|
+
if (this.ua) {
|
|
23626
|
+
try {
|
|
23627
|
+
this.ua.stop();
|
|
23628
|
+
} catch (e) {
|
|
23629
|
+
}
|
|
23630
|
+
this.ua = null;
|
|
23631
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
23632
|
+
}
|
|
23532
23633
|
try {
|
|
23533
23634
|
const socket = new import_jssip.default.WebSocketInterface(this.credentials.wsUrl);
|
|
23534
23635
|
const pcConfig = {
|
|
@@ -23540,16 +23641,18 @@ var DoraCell = class {
|
|
|
23540
23641
|
sockets: [socket],
|
|
23541
23642
|
register: true,
|
|
23542
23643
|
display_name: this.getDisplayName(),
|
|
23543
|
-
sessionTimers:
|
|
23644
|
+
sessionTimers: true,
|
|
23645
|
+
session_timers_refresh_method: "UPDATE",
|
|
23544
23646
|
trickleIce: false,
|
|
23545
23647
|
pcConfig,
|
|
23546
23648
|
instance_id: this.generateInstanceId()
|
|
23547
23649
|
};
|
|
23548
|
-
console.log("SDK: Initializing UA with config:", { ...uaConfig, password: "***" });
|
|
23549
23650
|
this.ua = new import_jssip.default.UA(uaConfig);
|
|
23550
23651
|
this.setupUserAgentHandlers();
|
|
23551
|
-
console.log("SDK: Starting UA...");
|
|
23552
23652
|
this.ua.start();
|
|
23653
|
+
if (this.callManager) {
|
|
23654
|
+
this.callManager.setUserAgent(this.ua);
|
|
23655
|
+
}
|
|
23553
23656
|
} catch (error) {
|
|
23554
23657
|
throw new ConnectionError(
|
|
23555
23658
|
`Failed to initialize User Agent: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
@@ -23569,6 +23672,7 @@ var DoraCell = class {
|
|
|
23569
23672
|
this.ua.on("registered", () => {
|
|
23570
23673
|
this.connectionStatus = "registered";
|
|
23571
23674
|
this.retryCount = 0;
|
|
23675
|
+
console.log(`Dora Cell SDK: Connected (${this.getDisplayName()})`);
|
|
23572
23676
|
this.emitConnectionStatus();
|
|
23573
23677
|
});
|
|
23574
23678
|
this.ua.on("registrationFailed", (e) => {
|
|
@@ -23583,9 +23687,7 @@ var DoraCell = class {
|
|
|
23583
23687
|
});
|
|
23584
23688
|
this.ua.on("newRTCSession", (e) => {
|
|
23585
23689
|
const session = e.session;
|
|
23586
|
-
console.log(`SDK: New session detected (${session.direction}):`, session.remote_identity?.uri?.toString());
|
|
23587
23690
|
if (session.direction === "incoming") {
|
|
23588
|
-
console.log("SDK: Handling incoming call event");
|
|
23589
23691
|
this.callManager?.handleIncomingCall(session);
|
|
23590
23692
|
}
|
|
23591
23693
|
});
|
|
@@ -23619,7 +23721,7 @@ var DoraCell = class {
|
|
|
23619
23721
|
/**
|
|
23620
23722
|
* Answer an incoming call
|
|
23621
23723
|
*/
|
|
23622
|
-
answerCall() {
|
|
23724
|
+
async answerCall() {
|
|
23623
23725
|
const currentCall = this.callManager?.getCurrentCall();
|
|
23624
23726
|
if (!currentCall) {
|
|
23625
23727
|
throw new CallError("No incoming call to answer");
|
|
@@ -23627,7 +23729,7 @@ var DoraCell = class {
|
|
|
23627
23729
|
if (currentCall.direction !== "inbound") {
|
|
23628
23730
|
throw new CallError("Current call is not an incoming call");
|
|
23629
23731
|
}
|
|
23630
|
-
this.callManager.answerCurrentCall();
|
|
23732
|
+
await this.callManager.answerCurrentCall();
|
|
23631
23733
|
}
|
|
23632
23734
|
/**
|
|
23633
23735
|
* Hangup the current call
|
|
@@ -23655,7 +23757,6 @@ var DoraCell = class {
|
|
|
23655
23757
|
return { balance: 0, currency: "NGN" };
|
|
23656
23758
|
}
|
|
23657
23759
|
try {
|
|
23658
|
-
console.log("SDK: Fetching wallet balance...");
|
|
23659
23760
|
const response = await this.apiClient.get("/wallets");
|
|
23660
23761
|
const wallets = Array.isArray(response) ? response : response.data || [];
|
|
23661
23762
|
if (wallets.length === 0) {
|
|
@@ -23663,11 +23764,11 @@ var DoraCell = class {
|
|
|
23663
23764
|
return { balance: 0, currency: "NGN" };
|
|
23664
23765
|
}
|
|
23665
23766
|
const primary = wallets[0];
|
|
23767
|
+
this.userId = primary.user_id || null;
|
|
23666
23768
|
const result = {
|
|
23667
|
-
balance: parseFloat(primary.balance || "0"),
|
|
23769
|
+
balance: parseFloat(primary.balance || primary.amount || "0"),
|
|
23668
23770
|
currency: primary.currency || "NGN"
|
|
23669
23771
|
};
|
|
23670
|
-
console.log("SDK: Wallet balance fetched:", result);
|
|
23671
23772
|
return result;
|
|
23672
23773
|
} catch (error) {
|
|
23673
23774
|
console.error("SDK: Failed to fetch wallet:", error);
|
|
@@ -23688,14 +23789,14 @@ var DoraCell = class {
|
|
|
23688
23789
|
throw new Error("SDK not authenticated. Call initialize() first.");
|
|
23689
23790
|
}
|
|
23690
23791
|
try {
|
|
23691
|
-
const
|
|
23792
|
+
const path = this.userId ? `/user/${this.userId}/extensions` : "/extensions";
|
|
23793
|
+
const response = await this.apiClient.get(path);
|
|
23692
23794
|
const extensions = response.data || response.extensions || response;
|
|
23693
23795
|
if (this.credentials && Array.isArray(extensions)) {
|
|
23694
23796
|
this.credentials.extensions = extensions;
|
|
23695
23797
|
}
|
|
23696
23798
|
return Array.isArray(extensions) ? extensions : [];
|
|
23697
23799
|
} catch (error) {
|
|
23698
|
-
console.error("SDK: Failed to fetch extensions:", error);
|
|
23699
23800
|
return this.credentials?.extensions || [];
|
|
23700
23801
|
}
|
|
23701
23802
|
}
|
|
@@ -23709,11 +23810,11 @@ var DoraCell = class {
|
|
|
23709
23810
|
* Update active extension and re-initialize SIP connection
|
|
23710
23811
|
*/
|
|
23711
23812
|
async setExtension(extension) {
|
|
23712
|
-
console.log(`SDK: Switching to extension ${extension}...`);
|
|
23713
23813
|
if (this.credentials) {
|
|
23714
|
-
const domain = this.credentials.sipDomain || "
|
|
23814
|
+
const domain = this.credentials.sipDomain || "64.227.10.164";
|
|
23715
23815
|
this.credentials.sipUri = `sip:${extension}@${domain}`;
|
|
23716
23816
|
await this.initializeUserAgent();
|
|
23817
|
+
await this.waitForRegistration();
|
|
23717
23818
|
this.emitConnectionStatus(this.connectionStatus);
|
|
23718
23819
|
}
|
|
23719
23820
|
}
|
|
@@ -23747,10 +23848,14 @@ var DoraCell = class {
|
|
|
23747
23848
|
}
|
|
23748
23849
|
// Helper methods
|
|
23749
23850
|
emitConnectionStatus(error) {
|
|
23750
|
-
|
|
23851
|
+
let activeExt = this.credentials?.sipUri ? extractNumberFromSipUri(this.credentials.sipUri) : void 0;
|
|
23852
|
+
if (!activeExt && this.credentials?.extensions && this.credentials.extensions.length > 0) {
|
|
23853
|
+
const primary = this.credentials.extensions.find((e) => e.isPrimary) || this.credentials.extensions[0];
|
|
23854
|
+
activeExt = primary.extension;
|
|
23855
|
+
}
|
|
23751
23856
|
const state = {
|
|
23752
23857
|
status: this.connectionStatus,
|
|
23753
|
-
extension,
|
|
23858
|
+
extension: activeExt,
|
|
23754
23859
|
error
|
|
23755
23860
|
};
|
|
23756
23861
|
this.events.emit("connection:status", state);
|
|
@@ -23776,6 +23881,12 @@ var DoraCell = class {
|
|
|
23776
23881
|
];
|
|
23777
23882
|
}
|
|
23778
23883
|
getDisplayName() {
|
|
23884
|
+
const currentExt = this.credentials?.sipUri ? extractNumberFromSipUri(this.credentials.sipUri) : null;
|
|
23885
|
+
if (currentExt && this.credentials?.extensions) {
|
|
23886
|
+
const found = this.credentials.extensions.find((e) => e.extension === currentExt);
|
|
23887
|
+
if (found?.displayName) return found.displayName;
|
|
23888
|
+
if (found?.extension) return `Ext ${found.extension}`;
|
|
23889
|
+
}
|
|
23779
23890
|
if (this.credentials?.extensions?.[0]?.displayName) {
|
|
23780
23891
|
return this.credentials.extensions[0].displayName;
|
|
23781
23892
|
}
|
|
@@ -23798,7 +23909,7 @@ var DoraCell = class {
|
|
|
23798
23909
|
case "staging":
|
|
23799
23910
|
case "dev":
|
|
23800
23911
|
return "https://dev.api.cell.usedora.com/api";
|
|
23801
|
-
|
|
23912
|
+
default:
|
|
23802
23913
|
return "https://api.cell.usedora.com/api";
|
|
23803
23914
|
}
|
|
23804
23915
|
}
|