tsvesync 1.0.95 → 1.0.97
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 +7 -0
- package/dist/lib/helpers.d.ts +64 -1
- package/dist/lib/helpers.js +198 -3
- package/dist/lib/vesync.d.ts +15 -1
- package/dist/lib/vesync.js +70 -37
- package/package.json +1 -1
- package/src/lib/helpers.ts +285 -2
- package/src/lib/vesync.ts +85 -49
- package/dist/__tests__/fans/vesyncAir131.test.d.ts +0 -4
- package/dist/__tests__/fans/vesyncAir131.test.js +0 -365
- package/dist/__tests__/fans/vesyncAirBaseV2.test.d.ts +0 -4
- package/dist/__tests__/fans/vesyncAirBaseV2.test.js +0 -283
- package/dist/__tests__/fans/vesyncAirBypass.test.d.ts +0 -4
- package/dist/__tests__/fans/vesyncAirBypass.test.js +0 -257
- package/dist/__tests__/vesyncFan.test.d.ts +0 -4
- package/dist/__tests__/vesyncFan.test.js +0 -263
package/src/lib/helpers.ts
CHANGED
|
@@ -7,8 +7,9 @@ import crypto from 'crypto';
|
|
|
7
7
|
import { VeSync } from './vesync';
|
|
8
8
|
import { logger } from './logger';
|
|
9
9
|
|
|
10
|
-
// API configuration -
|
|
10
|
+
// API configuration - Support regional endpoints
|
|
11
11
|
let _apiBaseUrl = 'https://smartapi.vesync.com';
|
|
12
|
+
let _currentRegion = 'US';
|
|
12
13
|
|
|
13
14
|
export function getApiBaseUrl(): string {
|
|
14
15
|
return _apiBaseUrl;
|
|
@@ -18,11 +19,39 @@ export function setApiBaseUrl(url: string): void {
|
|
|
18
19
|
_apiBaseUrl = url;
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
export function getCurrentRegion(): string {
|
|
23
|
+
return _currentRegion;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function setCurrentRegion(region: string): void {
|
|
27
|
+
_currentRegion = region;
|
|
28
|
+
if (region in REGION_ENDPOINTS) {
|
|
29
|
+
_apiBaseUrl = REGION_ENDPOINTS[region as keyof typeof REGION_ENDPOINTS];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Determine region from country code
|
|
34
|
+
export function getRegionFromCountryCode(countryCode: string): string {
|
|
35
|
+
const euCountries = [
|
|
36
|
+
'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR',
|
|
37
|
+
'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL',
|
|
38
|
+
'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'GB', 'IS', 'LI',
|
|
39
|
+
'NO', 'CH'
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
if (euCountries.includes(countryCode)) {
|
|
43
|
+
return 'EU';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Default to US for other countries
|
|
47
|
+
return 'US';
|
|
48
|
+
}
|
|
49
|
+
|
|
21
50
|
|
|
22
51
|
export const API_RATE_LIMIT = 30;
|
|
23
52
|
export const API_TIMEOUT = 15000;
|
|
24
53
|
|
|
25
|
-
export const APP_VERSION = '
|
|
54
|
+
export const APP_VERSION = '5.6.60';
|
|
26
55
|
export const PHONE_BRAND = 'SM N9005';
|
|
27
56
|
export const PHONE_OS = 'Android';
|
|
28
57
|
export const USER_TYPE = '1';
|
|
@@ -30,6 +59,30 @@ export const DEFAULT_TZ = 'America/New_York';
|
|
|
30
59
|
export const DEFAULT_REGION = 'US';
|
|
31
60
|
export const MOBILE_ID = '1234567890123456';
|
|
32
61
|
export const BYPASS_HEADER_UA = 'okhttp/3.12.1';
|
|
62
|
+
export const CLIENT_VERSION = 'VeSync 5.6.60';
|
|
63
|
+
|
|
64
|
+
// Regional API endpoints
|
|
65
|
+
export const REGION_ENDPOINTS = {
|
|
66
|
+
'US': 'https://smartapi.vesync.com',
|
|
67
|
+
'CA': 'https://smartapi.vesync.com',
|
|
68
|
+
'MX': 'https://smartapi.vesync.com',
|
|
69
|
+
'JP': 'https://smartapi.vesync.com',
|
|
70
|
+
'EU': 'https://smartapi.vesync.eu'
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Cross-region error code
|
|
74
|
+
export const CROSS_REGION_ERROR_CODE = -11260022;
|
|
75
|
+
export const APP_VERSION_TOO_LOW_ERROR_CODE = -11012022;
|
|
76
|
+
|
|
77
|
+
// Generate unique APP_ID for each session
|
|
78
|
+
export function generateAppId(): string {
|
|
79
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
80
|
+
let result = '';
|
|
81
|
+
for (let i = 0; i < 8; i++) {
|
|
82
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
33
86
|
|
|
34
87
|
export interface RequestBody {
|
|
35
88
|
acceptLanguage?: string;
|
|
@@ -50,9 +103,42 @@ export interface RequestBody {
|
|
|
50
103
|
userType?: string;
|
|
51
104
|
uuid?: string;
|
|
52
105
|
status?: string;
|
|
106
|
+
// New authentication parameters
|
|
107
|
+
authorizeCode?: string;
|
|
108
|
+
bizToken?: string;
|
|
109
|
+
regionChange?: string;
|
|
110
|
+
userCountryCode?: string;
|
|
111
|
+
authProtocolType?: string;
|
|
112
|
+
clientType?: string;
|
|
113
|
+
sourceAppID?: string;
|
|
114
|
+
appID?: string;
|
|
115
|
+
clientVersion?: string;
|
|
53
116
|
[key: string]: any;
|
|
54
117
|
}
|
|
55
118
|
|
|
119
|
+
// New authentication response interfaces
|
|
120
|
+
export interface AuthResponse {
|
|
121
|
+
code: number;
|
|
122
|
+
msg: string;
|
|
123
|
+
result?: {
|
|
124
|
+
authorizeCode?: string;
|
|
125
|
+
bizToken?: string;
|
|
126
|
+
userCountryCode?: string;
|
|
127
|
+
[key: string]: any;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface LoginResponse {
|
|
132
|
+
code: number;
|
|
133
|
+
msg: string;
|
|
134
|
+
result?: {
|
|
135
|
+
token?: string;
|
|
136
|
+
accountID?: string;
|
|
137
|
+
countryCode?: string;
|
|
138
|
+
[key: string]: any;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
56
142
|
export class Helpers {
|
|
57
143
|
static shouldRedact = true;
|
|
58
144
|
|
|
@@ -92,6 +178,17 @@ export class Helpers {
|
|
|
92
178
|
};
|
|
93
179
|
}
|
|
94
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Build header for new authentication endpoints
|
|
183
|
+
*/
|
|
184
|
+
static reqHeaderAuth(): Record<string, string> {
|
|
185
|
+
return {
|
|
186
|
+
'Content-Type': 'application/json; charset=UTF-8',
|
|
187
|
+
'User-Agent': BYPASS_HEADER_UA,
|
|
188
|
+
'accept-language': 'en'
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
95
192
|
/**
|
|
96
193
|
* Return universal keys for body of api requests
|
|
97
194
|
*/
|
|
@@ -127,6 +224,46 @@ export class Helpers {
|
|
|
127
224
|
};
|
|
128
225
|
}
|
|
129
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Build request body for initial authentication step
|
|
229
|
+
*/
|
|
230
|
+
static reqBodyAuthStep1(manager: VeSync, appId: string): Record<string, any> {
|
|
231
|
+
return {
|
|
232
|
+
'email': manager.username,
|
|
233
|
+
'password': this.hashPassword(manager.password),
|
|
234
|
+
'timeZone': manager.timeZone,
|
|
235
|
+
'acceptLanguage': 'en',
|
|
236
|
+
'appVersion': APP_VERSION,
|
|
237
|
+
'phoneBrand': PHONE_BRAND,
|
|
238
|
+
'phoneOS': PHONE_OS,
|
|
239
|
+
'traceId': Date.now().toString(),
|
|
240
|
+
'authProtocolType': 'pwd',
|
|
241
|
+
'clientType': 'android',
|
|
242
|
+
'sourceAppID': 'vesync_android',
|
|
243
|
+
'appID': appId,
|
|
244
|
+
'clientVersion': CLIENT_VERSION
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Build request body for second authentication step (login with authorize code)
|
|
250
|
+
*/
|
|
251
|
+
static reqBodyAuthStep2(authorizeCode: string, bizToken: string, appId: string, userCountryCode?: string): Record<string, any> {
|
|
252
|
+
const body: Record<string, any> = {
|
|
253
|
+
'authorizeCode': authorizeCode,
|
|
254
|
+
'bizToken': bizToken,
|
|
255
|
+
'appID': appId,
|
|
256
|
+
'clientVersion': CLIENT_VERSION,
|
|
257
|
+
'acceptLanguage': 'en'
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
if (userCountryCode) {
|
|
261
|
+
body.userCountryCode = userCountryCode;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return body;
|
|
265
|
+
}
|
|
266
|
+
|
|
130
267
|
/**
|
|
131
268
|
* Builder for body of api requests
|
|
132
269
|
*/
|
|
@@ -327,4 +464,150 @@ export class Helpers {
|
|
|
327
464
|
static md5(text: string): string {
|
|
328
465
|
return crypto.createHash('md5').update(text).digest('hex');
|
|
329
466
|
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Perform new two-step authentication flow
|
|
470
|
+
*/
|
|
471
|
+
static async authNewFlow(manager: VeSync, appId: string, region: string = 'US'): Promise<[boolean, string | null, string | null, string | null]> {
|
|
472
|
+
try {
|
|
473
|
+
// Step 1: Get authorization code
|
|
474
|
+
const step1Body = this.reqBodyAuthStep1(manager, appId);
|
|
475
|
+
const step1Headers = this.reqHeaderAuth();
|
|
476
|
+
|
|
477
|
+
// Set the correct regional endpoint
|
|
478
|
+
const originalUrl = getApiBaseUrl();
|
|
479
|
+
if (region in REGION_ENDPOINTS) {
|
|
480
|
+
setApiBaseUrl(REGION_ENDPOINTS[region as keyof typeof REGION_ENDPOINTS]);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
logger.debug('Step 1: Getting authorization code...', {
|
|
484
|
+
region,
|
|
485
|
+
endpoint: getApiBaseUrl(),
|
|
486
|
+
body: { ...step1Body, password: '[REDACTED]' }
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
const [authResponse, authStatus] = await this.callApi(
|
|
490
|
+
'/globalPlatform/api/accountAuth/v1/authByPWDOrOTM',
|
|
491
|
+
'post',
|
|
492
|
+
step1Body,
|
|
493
|
+
step1Headers,
|
|
494
|
+
manager
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
if (!authResponse || authStatus !== 200) {
|
|
498
|
+
logger.error('Step 1 failed:', authResponse);
|
|
499
|
+
setApiBaseUrl(originalUrl); // Restore original URL
|
|
500
|
+
return [false, null, null, null];
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (authResponse.code !== 0) {
|
|
504
|
+
logger.error('Step 1 error code:', authResponse.code, 'message:', authResponse.msg);
|
|
505
|
+
|
|
506
|
+
// Handle cross-region error
|
|
507
|
+
if (authResponse.code === CROSS_REGION_ERROR_CODE) {
|
|
508
|
+
logger.debug('Cross-region error detected, will try different region');
|
|
509
|
+
setApiBaseUrl(originalUrl); // Restore original URL
|
|
510
|
+
return [false, null, null, 'cross_region'];
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
setApiBaseUrl(originalUrl); // Restore original URL
|
|
514
|
+
return [false, null, null, null];
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const { authorizeCode, bizToken, userCountryCode } = authResponse.result || {};
|
|
518
|
+
|
|
519
|
+
if (!authorizeCode || !bizToken) {
|
|
520
|
+
logger.error('Missing required fields in step 1 response:', authResponse.result);
|
|
521
|
+
setApiBaseUrl(originalUrl); // Restore original URL
|
|
522
|
+
return [false, null, null, null];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
logger.debug('Step 1 successful, got authorization code');
|
|
526
|
+
|
|
527
|
+
// Step 2: Login with authorization code
|
|
528
|
+
const step2Body = this.reqBodyAuthStep2(authorizeCode, bizToken, appId, userCountryCode);
|
|
529
|
+
|
|
530
|
+
logger.debug('Step 2: Logging in with authorization code...');
|
|
531
|
+
|
|
532
|
+
const [loginResponse, loginStatus] = await this.callApi(
|
|
533
|
+
'/user/api/accountManage/v1/loginByAuthorizeCode4Vesync',
|
|
534
|
+
'post',
|
|
535
|
+
step2Body,
|
|
536
|
+
step1Headers,
|
|
537
|
+
manager
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
setApiBaseUrl(originalUrl); // Restore original URL
|
|
541
|
+
|
|
542
|
+
if (!loginResponse || loginStatus !== 200) {
|
|
543
|
+
logger.error('Step 2 failed:', loginResponse);
|
|
544
|
+
return [false, null, null, null];
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (loginResponse.code !== 0) {
|
|
548
|
+
logger.error('Step 2 error code:', loginResponse.code, 'message:', loginResponse.msg);
|
|
549
|
+
return [false, null, null, null];
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const { token, accountID, countryCode } = loginResponse.result || {};
|
|
553
|
+
|
|
554
|
+
if (!token || !accountID) {
|
|
555
|
+
logger.error('Missing required fields in step 2 response:', loginResponse.result);
|
|
556
|
+
return [false, null, null, null];
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
logger.debug('Step 2 successful, got token and accountID');
|
|
560
|
+
return [true, token, accountID, countryCode || userCountryCode];
|
|
561
|
+
|
|
562
|
+
} catch (error) {
|
|
563
|
+
logger.error('New authentication flow error:', error);
|
|
564
|
+
return [false, null, null, null];
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Perform legacy authentication (fallback)
|
|
570
|
+
*/
|
|
571
|
+
static async authLegacyFlow(manager: VeSync): Promise<[boolean, string | null, string | null, string | null]> {
|
|
572
|
+
try {
|
|
573
|
+
const body = this.reqBody(manager, 'login');
|
|
574
|
+
|
|
575
|
+
logger.debug('Legacy login attempt...', {
|
|
576
|
+
endpoint: getApiBaseUrl(),
|
|
577
|
+
body: { ...body, password: '[REDACTED]' }
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
const [response, status] = await this.callApi(
|
|
581
|
+
'/cloud/v1/user/login',
|
|
582
|
+
'post',
|
|
583
|
+
body,
|
|
584
|
+
{},
|
|
585
|
+
manager
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
if (!response || status !== 200) {
|
|
589
|
+
logger.error('Legacy login failed:', response);
|
|
590
|
+
return [false, null, null, null];
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (response.code && response.code !== 0) {
|
|
594
|
+
logger.error('Legacy login error code:', response.code, 'message:', response.msg);
|
|
595
|
+
return [false, null, null, null];
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const { token, accountID, countryCode } = response.result || {};
|
|
599
|
+
|
|
600
|
+
if (!token || !accountID) {
|
|
601
|
+
logger.error('Missing required fields in legacy login response:', response.result);
|
|
602
|
+
return [false, null, null, null];
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
logger.debug('Legacy login successful');
|
|
606
|
+
return [true, token, accountID, countryCode];
|
|
607
|
+
|
|
608
|
+
} catch (error) {
|
|
609
|
+
logger.error('Legacy authentication flow error:', error);
|
|
610
|
+
return [false, null, null, null];
|
|
611
|
+
}
|
|
612
|
+
}
|
|
330
613
|
}
|
package/src/lib/vesync.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* VeSync API Device Library
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { Helpers, API_RATE_LIMIT, DEFAULT_TZ, setApiBaseUrl, getApiBaseUrl } from './helpers';
|
|
5
|
+
import { Helpers, API_RATE_LIMIT, DEFAULT_TZ, setApiBaseUrl, getApiBaseUrl, generateAppId, getRegionFromCountryCode, setCurrentRegion, getCurrentRegion, REGION_ENDPOINTS } from './helpers';
|
|
6
6
|
import { VeSyncBaseDevice } from './vesyncBaseDevice';
|
|
7
7
|
import { fanModules } from './vesyncFanImpl';
|
|
8
8
|
import { outletModules } from './vesyncOutletImpl';
|
|
@@ -159,6 +159,8 @@ export class VeSync {
|
|
|
159
159
|
private _lastUpdateTs: number | null;
|
|
160
160
|
private _inProcess: boolean;
|
|
161
161
|
private _excludeConfig: ExcludeConfig | null;
|
|
162
|
+
private _appId: string;
|
|
163
|
+
private _region: string;
|
|
162
164
|
|
|
163
165
|
username: string;
|
|
164
166
|
password: string;
|
|
@@ -204,6 +206,8 @@ export class VeSync {
|
|
|
204
206
|
this._lastUpdateTs = null;
|
|
205
207
|
this._inProcess = false;
|
|
206
208
|
this._excludeConfig = excludeConfig || null;
|
|
209
|
+
this._appId = generateAppId();
|
|
210
|
+
this._region = 'US';
|
|
207
211
|
|
|
208
212
|
this.username = username;
|
|
209
213
|
this.password = password;
|
|
@@ -245,8 +249,9 @@ export class VeSync {
|
|
|
245
249
|
if (apiUrl) {
|
|
246
250
|
setApiBaseUrl(apiUrl);
|
|
247
251
|
} else {
|
|
248
|
-
//
|
|
252
|
+
// Start with US endpoint as default
|
|
249
253
|
setApiBaseUrl('https://smartapi.vesync.com');
|
|
254
|
+
setCurrentRegion('US');
|
|
250
255
|
}
|
|
251
256
|
|
|
252
257
|
// Set custom logger if provided
|
|
@@ -299,6 +304,30 @@ export class VeSync {
|
|
|
299
304
|
}
|
|
300
305
|
}
|
|
301
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Get current App ID
|
|
309
|
+
*/
|
|
310
|
+
get appId(): string {
|
|
311
|
+
return this._appId;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Get current region
|
|
316
|
+
*/
|
|
317
|
+
get region(): string {
|
|
318
|
+
return this._region;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Set region and update API endpoint
|
|
323
|
+
*/
|
|
324
|
+
set region(region: string) {
|
|
325
|
+
if (region in REGION_ENDPOINTS) {
|
|
326
|
+
this._region = region;
|
|
327
|
+
setCurrentRegion(region);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
302
331
|
/**
|
|
303
332
|
* Test if device should be removed
|
|
304
333
|
*/
|
|
@@ -506,75 +535,82 @@ export class VeSync {
|
|
|
506
535
|
}
|
|
507
536
|
|
|
508
537
|
/**
|
|
509
|
-
* Login to VeSync server
|
|
538
|
+
* Login to VeSync server with new authentication flow and backwards compatibility
|
|
510
539
|
*/
|
|
511
540
|
async login(retryAttempts: number = 3, initialDelayMs: number = 1000): Promise<boolean> {
|
|
512
|
-
|
|
513
|
-
|
|
541
|
+
logger.debug('Starting VeSync authentication...', {
|
|
542
|
+
username: this.username,
|
|
543
|
+
appId: this._appId,
|
|
544
|
+
region: this._region
|
|
545
|
+
});
|
|
546
|
+
|
|
514
547
|
for (let attempt = 0; attempt < retryAttempts; attempt++) {
|
|
515
548
|
try {
|
|
516
|
-
logger.debug(
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
if (response && response.code) {
|
|
535
|
-
switch (response.code) {
|
|
536
|
-
case -11012022:
|
|
537
|
-
logger.error('App version too low error. Current version:', body.appVersion);
|
|
538
|
-
logger.error('This typically indicates an API version compatibility issue.');
|
|
539
|
-
break;
|
|
540
|
-
case -11003:
|
|
541
|
-
logger.error('Authentication failed - check credentials');
|
|
542
|
-
break;
|
|
543
|
-
case -11001:
|
|
544
|
-
logger.error('Invalid request format');
|
|
545
|
-
break;
|
|
546
|
-
default:
|
|
547
|
-
logger.error('API error code:', response.code, 'message:', response.msg);
|
|
549
|
+
logger.debug(`Authentication attempt ${attempt + 1} of ${retryAttempts}`);
|
|
550
|
+
|
|
551
|
+
// Try new authentication flow first
|
|
552
|
+
let [success, token, accountId, countryCode] = await Helpers.authNewFlow(this, this._appId, this._region);
|
|
553
|
+
|
|
554
|
+
if (!success) {
|
|
555
|
+
// Check if we need to try a different region
|
|
556
|
+
if (countryCode === 'cross_region') {
|
|
557
|
+
logger.debug('Cross-region error detected, trying EU region...');
|
|
558
|
+
this._region = 'EU';
|
|
559
|
+
setCurrentRegion('EU');
|
|
560
|
+
[success, token, accountId, countryCode] = await Helpers.authNewFlow(this, this._appId, 'EU');
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// If new flow still fails, try legacy authentication as fallback
|
|
564
|
+
if (!success) {
|
|
565
|
+
logger.debug('New authentication flow failed, trying legacy flow...');
|
|
566
|
+
[success, token, accountId, countryCode] = await Helpers.authLegacyFlow(this);
|
|
548
567
|
}
|
|
549
568
|
}
|
|
550
569
|
|
|
551
|
-
if (
|
|
552
|
-
this.token =
|
|
553
|
-
this.accountId =
|
|
554
|
-
this.countryCode =
|
|
570
|
+
if (success && token && accountId) {
|
|
571
|
+
this.token = token;
|
|
572
|
+
this.accountId = accountId;
|
|
573
|
+
this.countryCode = countryCode;
|
|
555
574
|
this.enabled = true;
|
|
556
|
-
|
|
575
|
+
|
|
576
|
+
// Set region based on country code if we got one
|
|
577
|
+
if (countryCode) {
|
|
578
|
+
const detectedRegion = getRegionFromCountryCode(countryCode);
|
|
579
|
+
if (detectedRegion !== this._region) {
|
|
580
|
+
logger.debug(`Updating region from ${this._region} to ${detectedRegion} based on country code: ${countryCode}`);
|
|
581
|
+
this._region = detectedRegion;
|
|
582
|
+
setCurrentRegion(detectedRegion);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
logger.debug('Authentication successful!', {
|
|
587
|
+
region: this._region,
|
|
588
|
+
countryCode: countryCode,
|
|
589
|
+
endpoint: getApiBaseUrl()
|
|
590
|
+
});
|
|
557
591
|
return true;
|
|
558
592
|
}
|
|
559
593
|
|
|
560
|
-
// If we reach here,
|
|
594
|
+
// If we reach here, authentication failed
|
|
561
595
|
const delay = initialDelayMs * Math.pow(2, attempt);
|
|
562
|
-
logger.debug(`
|
|
596
|
+
logger.debug(`Authentication attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
|
|
563
597
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
564
|
-
|
|
598
|
+
|
|
565
599
|
} catch (error) {
|
|
600
|
+
logger.error(`Authentication attempt ${attempt + 1} error:`, error);
|
|
601
|
+
|
|
566
602
|
if (attempt === retryAttempts - 1) {
|
|
567
|
-
logger.error('
|
|
603
|
+
logger.error('Authentication failed after all retry attempts');
|
|
568
604
|
return false;
|
|
569
605
|
}
|
|
570
|
-
|
|
606
|
+
|
|
571
607
|
const delay = initialDelayMs * Math.pow(2, attempt);
|
|
572
|
-
logger.debug(`
|
|
608
|
+
logger.debug(`Retrying authentication in ${delay}ms...`);
|
|
573
609
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
574
610
|
}
|
|
575
611
|
}
|
|
576
612
|
|
|
577
|
-
logger.error('Unable to
|
|
613
|
+
logger.error('Unable to authenticate with supplied credentials after all retry attempts');
|
|
578
614
|
return false;
|
|
579
615
|
}
|
|
580
616
|
|