tsvesync 1.0.102 → 1.0.103
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.ts +1 -0
- package/dist/index.js +7 -1
- package/dist/lib/constants.d.ts +14 -0
- package/dist/lib/constants.js +35 -0
- package/dist/lib/helpers.d.ts +0 -1
- package/dist/lib/helpers.js +29 -47
- package/dist/lib/vesync.js +25 -8
- package/package.json +1 -1
- package/src/index.ts +9 -1
- package/src/lib/constants.ts +35 -0
- package/src/lib/helpers.ts +31 -56
- package/src/lib/vesync.ts +28 -8
package/dist/index.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export { VeSyncBulb } from './lib/vesyncBulb';
|
|
|
5
5
|
export { VeSyncWallSwitch } from './lib/vesyncSwitchImpl';
|
|
6
6
|
export { VeSyncBaseDevice } from './lib/vesyncBaseDevice';
|
|
7
7
|
export { Helpers, setApiBaseUrl } from './lib/helpers';
|
|
8
|
+
export { CREDENTIAL_ERROR_CODES, CROSS_REGION_ERROR_CODES, isCredentialError, isCrossRegionError } from './lib/constants';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setApiBaseUrl = exports.Helpers = exports.VeSyncBaseDevice = exports.VeSyncWallSwitch = exports.VeSyncBulb = exports.VeSyncOutdoorPlug = exports.VeSyncOutlet15A = exports.VeSyncOutlet10A = exports.VeSyncOutlet7A = exports.VeSyncAirBypass = exports.VeSync = void 0;
|
|
3
|
+
exports.isCrossRegionError = exports.isCredentialError = exports.CROSS_REGION_ERROR_CODES = exports.CREDENTIAL_ERROR_CODES = exports.setApiBaseUrl = exports.Helpers = exports.VeSyncBaseDevice = exports.VeSyncWallSwitch = exports.VeSyncBulb = exports.VeSyncOutdoorPlug = exports.VeSyncOutlet15A = exports.VeSyncOutlet10A = exports.VeSyncOutlet7A = exports.VeSyncAirBypass = exports.VeSync = void 0;
|
|
4
4
|
// Export main VeSync manager class
|
|
5
5
|
var vesync_1 = require("./lib/vesync");
|
|
6
6
|
Object.defineProperty(exports, "VeSync", { enumerable: true, get: function () { return vesync_1.VeSync; } });
|
|
@@ -23,3 +23,9 @@ Object.defineProperty(exports, "VeSyncBaseDevice", { enumerable: true, get: func
|
|
|
23
23
|
var helpers_1 = require("./lib/helpers");
|
|
24
24
|
Object.defineProperty(exports, "Helpers", { enumerable: true, get: function () { return helpers_1.Helpers; } });
|
|
25
25
|
Object.defineProperty(exports, "setApiBaseUrl", { enumerable: true, get: function () { return helpers_1.setApiBaseUrl; } });
|
|
26
|
+
// Export constants
|
|
27
|
+
var constants_1 = require("./lib/constants");
|
|
28
|
+
Object.defineProperty(exports, "CREDENTIAL_ERROR_CODES", { enumerable: true, get: function () { return constants_1.CREDENTIAL_ERROR_CODES; } });
|
|
29
|
+
Object.defineProperty(exports, "CROSS_REGION_ERROR_CODES", { enumerable: true, get: function () { return constants_1.CROSS_REGION_ERROR_CODES; } });
|
|
30
|
+
Object.defineProperty(exports, "isCredentialError", { enumerable: true, get: function () { return constants_1.isCredentialError; } });
|
|
31
|
+
Object.defineProperty(exports, "isCrossRegionError", { enumerable: true, get: function () { return constants_1.isCrossRegionError; } });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VeSync API Error Codes
|
|
3
|
+
*/
|
|
4
|
+
export declare const CREDENTIAL_ERROR_CODES: number[];
|
|
5
|
+
export declare const CROSS_REGION_ERROR_CODES: number[];
|
|
6
|
+
export declare const APP_VERSION_ERROR_CODES: number[];
|
|
7
|
+
/**
|
|
8
|
+
* Check if an error code indicates bad credentials
|
|
9
|
+
*/
|
|
10
|
+
export declare function isCredentialError(code: number): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Check if an error code indicates cross-region issue
|
|
13
|
+
*/
|
|
14
|
+
export declare function isCrossRegionError(code: number): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VeSync API Error Codes
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.APP_VERSION_ERROR_CODES = exports.CROSS_REGION_ERROR_CODES = exports.CREDENTIAL_ERROR_CODES = void 0;
|
|
7
|
+
exports.isCredentialError = isCredentialError;
|
|
8
|
+
exports.isCrossRegionError = isCrossRegionError;
|
|
9
|
+
// Authentication error codes that indicate bad credentials (no point retrying)
|
|
10
|
+
exports.CREDENTIAL_ERROR_CODES = [
|
|
11
|
+
-11201129, // account or password incorrect
|
|
12
|
+
-11202129, // the account does not exist
|
|
13
|
+
-11000129, // illegal argument (empty credentials)
|
|
14
|
+
];
|
|
15
|
+
// Cross-region error codes that require region switching
|
|
16
|
+
exports.CROSS_REGION_ERROR_CODES = [
|
|
17
|
+
-11260022, // cross region error
|
|
18
|
+
-11261022, // access region conflict error
|
|
19
|
+
];
|
|
20
|
+
// Other error codes
|
|
21
|
+
exports.APP_VERSION_ERROR_CODES = [
|
|
22
|
+
-11012022, // app version is too low
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* Check if an error code indicates bad credentials
|
|
26
|
+
*/
|
|
27
|
+
function isCredentialError(code) {
|
|
28
|
+
return exports.CREDENTIAL_ERROR_CODES.includes(code);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if an error code indicates cross-region issue
|
|
32
|
+
*/
|
|
33
|
+
function isCrossRegionError(code) {
|
|
34
|
+
return exports.CROSS_REGION_ERROR_CODES.includes(code);
|
|
35
|
+
}
|
package/dist/lib/helpers.d.ts
CHANGED
|
@@ -26,7 +26,6 @@ export declare const REGION_ENDPOINTS: {
|
|
|
26
26
|
JP: string;
|
|
27
27
|
EU: string;
|
|
28
28
|
};
|
|
29
|
-
export declare const CROSS_REGION_ERROR_CODES: number[];
|
|
30
29
|
export declare const APP_VERSION_TOO_LOW_ERROR_CODE = -11012022;
|
|
31
30
|
export declare const AUTH_ERROR_CODES: {
|
|
32
31
|
ACCOUNT_PASSWORD_INCORRECT: number;
|
package/dist/lib/helpers.js
CHANGED
|
@@ -6,7 +6,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
6
6
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
7
|
};
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.Helpers = exports.AUTH_ERROR_CODES = exports.APP_VERSION_TOO_LOW_ERROR_CODE = exports.
|
|
9
|
+
exports.Helpers = exports.AUTH_ERROR_CODES = exports.APP_VERSION_TOO_LOW_ERROR_CODE = exports.REGION_ENDPOINTS = exports.CLIENT_VERSION = exports.BYPASS_HEADER_UA = exports.MOBILE_ID = exports.DEFAULT_REGION = exports.DEFAULT_TZ = exports.USER_TYPE = exports.CLIENT_INFO = exports.PHONE_OS = exports.PHONE_BRAND = exports.APP_VERSION = exports.API_TIMEOUT = exports.API_RATE_LIMIT = void 0;
|
|
10
10
|
exports.getApiBaseUrl = getApiBaseUrl;
|
|
11
11
|
exports.setApiBaseUrl = setApiBaseUrl;
|
|
12
12
|
exports.getCurrentRegion = getCurrentRegion;
|
|
@@ -19,6 +19,7 @@ exports.generateTerminalId = generateTerminalId;
|
|
|
19
19
|
const axios_1 = __importDefault(require("axios"));
|
|
20
20
|
const crypto_1 = __importDefault(require("crypto"));
|
|
21
21
|
const logger_1 = require("./logger");
|
|
22
|
+
const constants_1 = require("./constants");
|
|
22
23
|
// API configuration - Support regional endpoints
|
|
23
24
|
let _apiBaseUrl = 'https://smartapi.vesync.com';
|
|
24
25
|
let _currentRegion = 'US';
|
|
@@ -72,7 +73,7 @@ exports.REGION_ENDPOINTS = {
|
|
|
72
73
|
'EU': 'https://smartapi.vesync.eu'
|
|
73
74
|
};
|
|
74
75
|
// Cross-region error codes - multiple versions exist
|
|
75
|
-
|
|
76
|
+
// CROSS_REGION_ERROR_CODES moved to constants.ts
|
|
76
77
|
exports.APP_VERSION_TOO_LOW_ERROR_CODE = -11012022;
|
|
77
78
|
// Authentication error codes
|
|
78
79
|
exports.AUTH_ERROR_CODES = {
|
|
@@ -480,8 +481,14 @@ class Helpers {
|
|
|
480
481
|
}
|
|
481
482
|
if (authResponse.code !== 0) {
|
|
482
483
|
logger_1.logger.error('Step 1 error code:', authResponse.code, 'message:', authResponse.msg);
|
|
484
|
+
// Check if it's a credential error (no point retrying)
|
|
485
|
+
if ((0, constants_1.isCredentialError)(authResponse.code)) {
|
|
486
|
+
logger_1.logger.error('Credential error detected - invalid username or password');
|
|
487
|
+
setApiBaseUrl(originalUrl); // Restore original URL
|
|
488
|
+
return [false, null, null, 'credential_error'];
|
|
489
|
+
}
|
|
483
490
|
// Handle cross-region error (multiple error codes possible)
|
|
484
|
-
if (
|
|
491
|
+
if ((0, constants_1.isCrossRegionError)(authResponse.code)) {
|
|
485
492
|
logger_1.logger.debug('Cross-region error detected:', authResponse.code, 'will try different region');
|
|
486
493
|
setApiBaseUrl(originalUrl); // Restore original URL
|
|
487
494
|
return [false, null, null, 'cross_region'];
|
|
@@ -502,56 +509,26 @@ class Helpers {
|
|
|
502
509
|
const step2Body = this.reqBodyAuthStep2(authorizeCode, bizToken, appId, terminalId, countryCodeForStep2);
|
|
503
510
|
logger_1.logger.debug('Step 2: Logging in with authorization code...');
|
|
504
511
|
const [loginResponse, loginStatus] = await this.callApi('/user/api/accountManage/v1/loginByAuthorizeCode4Vesync', 'post', step2Body, step1Headers, manager);
|
|
512
|
+
logger_1.logger.debug('Step 2 response:', {
|
|
513
|
+
status: loginStatus,
|
|
514
|
+
code: loginResponse === null || loginResponse === void 0 ? void 0 : loginResponse.code,
|
|
515
|
+
hasResult: !!(loginResponse === null || loginResponse === void 0 ? void 0 : loginResponse.result),
|
|
516
|
+
msg: loginResponse === null || loginResponse === void 0 ? void 0 : loginResponse.msg
|
|
517
|
+
});
|
|
505
518
|
if (!loginResponse || loginStatus !== 200) {
|
|
506
519
|
logger_1.logger.error('Step 2 failed:', loginResponse);
|
|
507
520
|
setApiBaseUrl(originalUrl); // Restore original URL on failure
|
|
508
521
|
return [false, null, null, null];
|
|
509
522
|
}
|
|
510
523
|
if (loginResponse.code !== 0) {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
method: 'loginByAuthorizeCode4Vesync',
|
|
520
|
-
authorizeCode: authorizeCode,
|
|
521
|
-
acceptLanguage: 'en',
|
|
522
|
-
clientInfo: exports.CLIENT_INFO,
|
|
523
|
-
clientType: 'vesyncApp',
|
|
524
|
-
clientVersion: exports.CLIENT_VERSION,
|
|
525
|
-
debugMode: false,
|
|
526
|
-
emailSubscriptions: false,
|
|
527
|
-
osInfo: exports.PHONE_OS,
|
|
528
|
-
terminalId: terminalId,
|
|
529
|
-
timeZone: exports.DEFAULT_TZ,
|
|
530
|
-
userCountryCode: newCountryCode,
|
|
531
|
-
traceId: `APP${appId}${Math.floor(Date.now() / 1000)}`,
|
|
532
|
-
bizToken: regionChangeToken,
|
|
533
|
-
regionChange: 'last_region'
|
|
534
|
-
};
|
|
535
|
-
logger_1.logger.debug('Retrying Step 2 with region change token...', {
|
|
536
|
-
newCountryCode,
|
|
537
|
-
currentEndpoint: getApiBaseUrl()
|
|
538
|
-
});
|
|
539
|
-
const [retryResponse, retryStatus] = await this.callApi('/user/api/accountManage/v1/loginByAuthorizeCode4Vesync', 'post', retryBody, step1Headers, manager);
|
|
540
|
-
if (retryResponse && retryResponse.code === 0) {
|
|
541
|
-
const { token, accountID, countryCode } = retryResponse.result || {};
|
|
542
|
-
if (token && accountID) {
|
|
543
|
-
logger_1.logger.debug('Step 2 retry successful with region change');
|
|
544
|
-
setApiBaseUrl(originalUrl); // Restore original URL on success
|
|
545
|
-
return [true, token, accountID, countryCode || newCountryCode];
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
logger_1.logger.error('Step 2 retry failed:', retryResponse);
|
|
549
|
-
setApiBaseUrl(originalUrl); // Restore original URL on failure
|
|
550
|
-
return [false, null, null, null];
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
logger_1.logger.error('No region change token in cross-region error response');
|
|
554
|
-
}
|
|
524
|
+
logger_1.logger.debug('Step 2 error, code:', loginResponse.code, 'checking if cross-region...');
|
|
525
|
+
// Handle cross-region error - for EU accounts, we need to switch regions entirely
|
|
526
|
+
if (constants_1.CROSS_REGION_ERROR_CODES.includes(loginResponse.code)) {
|
|
527
|
+
logger_1.logger.debug('Cross-region error detected in Step 2 - need to use different region');
|
|
528
|
+
// For cross-region errors, we need to retry the entire flow with a different region
|
|
529
|
+
// The authorization code from Step 1 is tied to the endpoint it was obtained from
|
|
530
|
+
setApiBaseUrl(originalUrl); // Restore original URL
|
|
531
|
+
return [false, null, null, 'cross_region_retry'];
|
|
555
532
|
}
|
|
556
533
|
logger_1.logger.error('Step 2 error code:', loginResponse.code, 'message:', loginResponse.msg);
|
|
557
534
|
setApiBaseUrl(originalUrl); // Restore original URL on error
|
|
@@ -588,6 +565,11 @@ class Helpers {
|
|
|
588
565
|
}
|
|
589
566
|
if (response.code && response.code !== 0) {
|
|
590
567
|
logger_1.logger.error('Legacy login error code:', response.code, 'message:', response.msg);
|
|
568
|
+
// Check if it's a credential error
|
|
569
|
+
if ((0, constants_1.isCredentialError)(response.code)) {
|
|
570
|
+
logger_1.logger.error('Legacy auth: Credential error detected');
|
|
571
|
+
return [false, null, null, 'credential_error'];
|
|
572
|
+
}
|
|
591
573
|
return [false, null, null, null];
|
|
592
574
|
}
|
|
593
575
|
const { token, accountID, countryCode } = response.result || {};
|
package/dist/lib/vesync.js
CHANGED
|
@@ -461,13 +461,8 @@ class VeSync {
|
|
|
461
461
|
for (let attempt = 0; attempt < retryAttempts; attempt++) {
|
|
462
462
|
try {
|
|
463
463
|
logger_1.logger.debug(`Authentication attempt ${attempt + 1} of ${retryAttempts}`);
|
|
464
|
-
//
|
|
465
|
-
|
|
466
|
-
if (detectedRegion !== this._region) {
|
|
467
|
-
logger_1.logger.debug(`Adjusting region from ${this._region} to ${detectedRegion} based on email domain`);
|
|
468
|
-
this._region = detectedRegion;
|
|
469
|
-
(0, helpers_1.setCurrentRegion)(detectedRegion);
|
|
470
|
-
}
|
|
464
|
+
// Skip email-based region detection for now since we'll handle cross-region errors
|
|
465
|
+
// The cross-region error handling below will automatically switch to the correct region
|
|
471
466
|
// Try new authentication flow first with detected region
|
|
472
467
|
let [success, token, accountId, countryCode] = await helpers_1.Helpers.authNewFlow(this, this._appId, this._region);
|
|
473
468
|
// Track if new flow succeeded
|
|
@@ -475,15 +470,32 @@ class VeSync {
|
|
|
475
470
|
this.authFlowUsed = 'new';
|
|
476
471
|
}
|
|
477
472
|
if (!success) {
|
|
473
|
+
// Check if it's a credential error (no point retrying)
|
|
474
|
+
if (countryCode === 'credential_error') {
|
|
475
|
+
logger_1.logger.error('Authentication failed due to invalid credentials');
|
|
476
|
+
return false; // Exit immediately, don't retry
|
|
477
|
+
}
|
|
478
478
|
// Check if we need to try a different region
|
|
479
|
-
if (countryCode === 'cross_region') {
|
|
479
|
+
if (countryCode === 'cross_region' || countryCode === 'cross_region_retry') {
|
|
480
480
|
logger_1.logger.debug('Cross-region error detected, trying opposite region...');
|
|
481
481
|
const alternateRegion = this._region === 'US' ? 'EU' : 'US';
|
|
482
|
+
logger_1.logger.debug(`Switching from ${this._region} to ${alternateRegion} region`);
|
|
482
483
|
this._region = alternateRegion;
|
|
483
484
|
(0, helpers_1.setCurrentRegion)(alternateRegion);
|
|
485
|
+
// Update the API endpoint for the new region
|
|
486
|
+
const regionEndpoints = {
|
|
487
|
+
'US': 'https://smartapi.vesync.com',
|
|
488
|
+
'EU': 'https://smartapi.vesync.eu'
|
|
489
|
+
};
|
|
490
|
+
if (alternateRegion in regionEndpoints) {
|
|
491
|
+
(0, helpers_1.setApiBaseUrl)(regionEndpoints[alternateRegion]);
|
|
492
|
+
logger_1.logger.debug(`API endpoint set to ${regionEndpoints[alternateRegion]}`);
|
|
493
|
+
}
|
|
494
|
+
// Retry with the alternate region
|
|
484
495
|
[success, token, accountId, countryCode] = await helpers_1.Helpers.authNewFlow(this, this._appId, alternateRegion);
|
|
485
496
|
if (success) {
|
|
486
497
|
this.authFlowUsed = 'new';
|
|
498
|
+
logger_1.logger.debug(`Authentication successful with ${alternateRegion} region`);
|
|
487
499
|
}
|
|
488
500
|
}
|
|
489
501
|
// If new flow still fails, try legacy authentication as fallback
|
|
@@ -493,6 +505,11 @@ class VeSync {
|
|
|
493
505
|
if (success) {
|
|
494
506
|
this.authFlowUsed = 'legacy';
|
|
495
507
|
}
|
|
508
|
+
else if (countryCode === 'credential_error') {
|
|
509
|
+
// Legacy auth also detected bad credentials
|
|
510
|
+
logger_1.logger.error('Both authentication methods report invalid credentials');
|
|
511
|
+
return false; // Exit immediately
|
|
512
|
+
}
|
|
496
513
|
}
|
|
497
514
|
}
|
|
498
515
|
if (success && token && accountId) {
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -11,4 +11,12 @@ export { VeSyncWallSwitch } from './lib/vesyncSwitchImpl';
|
|
|
11
11
|
export { VeSyncBaseDevice } from './lib/vesyncBaseDevice';
|
|
12
12
|
|
|
13
13
|
// Export helper functions
|
|
14
|
-
export { Helpers, setApiBaseUrl } from './lib/helpers';
|
|
14
|
+
export { Helpers, setApiBaseUrl } from './lib/helpers';
|
|
15
|
+
|
|
16
|
+
// Export constants
|
|
17
|
+
export {
|
|
18
|
+
CREDENTIAL_ERROR_CODES,
|
|
19
|
+
CROSS_REGION_ERROR_CODES,
|
|
20
|
+
isCredentialError,
|
|
21
|
+
isCrossRegionError
|
|
22
|
+
} from './lib/constants';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VeSync API Error Codes
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Authentication error codes that indicate bad credentials (no point retrying)
|
|
6
|
+
export const CREDENTIAL_ERROR_CODES = [
|
|
7
|
+
-11201129, // account or password incorrect
|
|
8
|
+
-11202129, // the account does not exist
|
|
9
|
+
-11000129, // illegal argument (empty credentials)
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
// Cross-region error codes that require region switching
|
|
13
|
+
export const CROSS_REGION_ERROR_CODES = [
|
|
14
|
+
-11260022, // cross region error
|
|
15
|
+
-11261022, // access region conflict error
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// Other error codes
|
|
19
|
+
export const APP_VERSION_ERROR_CODES = [
|
|
20
|
+
-11012022, // app version is too low
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Check if an error code indicates bad credentials
|
|
25
|
+
*/
|
|
26
|
+
export function isCredentialError(code: number): boolean {
|
|
27
|
+
return CREDENTIAL_ERROR_CODES.includes(code);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if an error code indicates cross-region issue
|
|
32
|
+
*/
|
|
33
|
+
export function isCrossRegionError(code: number): boolean {
|
|
34
|
+
return CROSS_REGION_ERROR_CODES.includes(code);
|
|
35
|
+
}
|
package/src/lib/helpers.ts
CHANGED
|
@@ -6,6 +6,7 @@ import axios from 'axios';
|
|
|
6
6
|
import crypto from 'crypto';
|
|
7
7
|
import { VeSync } from './vesync';
|
|
8
8
|
import { logger } from './logger';
|
|
9
|
+
import { isCredentialError, isCrossRegionError, CROSS_REGION_ERROR_CODES } from './constants';
|
|
9
10
|
|
|
10
11
|
// API configuration - Support regional endpoints
|
|
11
12
|
let _apiBaseUrl = 'https://smartapi.vesync.com';
|
|
@@ -72,7 +73,7 @@ export const REGION_ENDPOINTS = {
|
|
|
72
73
|
};
|
|
73
74
|
|
|
74
75
|
// Cross-region error codes - multiple versions exist
|
|
75
|
-
|
|
76
|
+
// CROSS_REGION_ERROR_CODES moved to constants.ts
|
|
76
77
|
export const APP_VERSION_TOO_LOW_ERROR_CODE = -11012022;
|
|
77
78
|
|
|
78
79
|
// Authentication error codes
|
|
@@ -594,8 +595,15 @@ export class Helpers {
|
|
|
594
595
|
if (authResponse.code !== 0) {
|
|
595
596
|
logger.error('Step 1 error code:', authResponse.code, 'message:', authResponse.msg);
|
|
596
597
|
|
|
598
|
+
// Check if it's a credential error (no point retrying)
|
|
599
|
+
if (isCredentialError(authResponse.code)) {
|
|
600
|
+
logger.error('Credential error detected - invalid username or password');
|
|
601
|
+
setApiBaseUrl(originalUrl); // Restore original URL
|
|
602
|
+
return [false, null, null, 'credential_error'];
|
|
603
|
+
}
|
|
604
|
+
|
|
597
605
|
// Handle cross-region error (multiple error codes possible)
|
|
598
|
-
if (
|
|
606
|
+
if (isCrossRegionError(authResponse.code)) {
|
|
599
607
|
logger.debug('Cross-region error detected:', authResponse.code, 'will try different region');
|
|
600
608
|
setApiBaseUrl(originalUrl); // Restore original URL
|
|
601
609
|
return [false, null, null, 'cross_region'];
|
|
@@ -630,6 +638,13 @@ export class Helpers {
|
|
|
630
638
|
manager
|
|
631
639
|
);
|
|
632
640
|
|
|
641
|
+
logger.debug('Step 2 response:', {
|
|
642
|
+
status: loginStatus,
|
|
643
|
+
code: loginResponse?.code,
|
|
644
|
+
hasResult: !!loginResponse?.result,
|
|
645
|
+
msg: loginResponse?.msg
|
|
646
|
+
});
|
|
647
|
+
|
|
633
648
|
if (!loginResponse || loginStatus !== 200) {
|
|
634
649
|
logger.error('Step 2 failed:', loginResponse);
|
|
635
650
|
setApiBaseUrl(originalUrl); // Restore original URL on failure
|
|
@@ -637,61 +652,14 @@ export class Helpers {
|
|
|
637
652
|
}
|
|
638
653
|
|
|
639
654
|
if (loginResponse.code !== 0) {
|
|
640
|
-
|
|
655
|
+
logger.debug('Step 2 error, code:', loginResponse.code, 'checking if cross-region...');
|
|
656
|
+
// Handle cross-region error - for EU accounts, we need to switch regions entirely
|
|
641
657
|
if (CROSS_REGION_ERROR_CODES.includes(loginResponse.code)) {
|
|
642
|
-
logger.debug('Cross-region error detected in Step 2
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
// Retry Step 2 with region change token on the SAME endpoint
|
|
648
|
-
// The region change token tells the server to handle the cross-region authentication
|
|
649
|
-
const retryBody = {
|
|
650
|
-
method: 'loginByAuthorizeCode4Vesync',
|
|
651
|
-
authorizeCode: authorizeCode,
|
|
652
|
-
acceptLanguage: 'en',
|
|
653
|
-
clientInfo: CLIENT_INFO,
|
|
654
|
-
clientType: 'vesyncApp',
|
|
655
|
-
clientVersion: CLIENT_VERSION,
|
|
656
|
-
debugMode: false,
|
|
657
|
-
emailSubscriptions: false,
|
|
658
|
-
osInfo: PHONE_OS,
|
|
659
|
-
terminalId: terminalId,
|
|
660
|
-
timeZone: DEFAULT_TZ,
|
|
661
|
-
userCountryCode: newCountryCode,
|
|
662
|
-
traceId: `APP${appId}${Math.floor(Date.now() / 1000)}`,
|
|
663
|
-
bizToken: regionChangeToken,
|
|
664
|
-
regionChange: 'last_region'
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
logger.debug('Retrying Step 2 with region change token...', {
|
|
668
|
-
newCountryCode,
|
|
669
|
-
currentEndpoint: getApiBaseUrl()
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
const [retryResponse, retryStatus] = await this.callApi(
|
|
673
|
-
'/user/api/accountManage/v1/loginByAuthorizeCode4Vesync',
|
|
674
|
-
'post',
|
|
675
|
-
retryBody,
|
|
676
|
-
step1Headers,
|
|
677
|
-
manager
|
|
678
|
-
);
|
|
679
|
-
|
|
680
|
-
if (retryResponse && retryResponse.code === 0) {
|
|
681
|
-
const { token, accountID, countryCode } = retryResponse.result || {};
|
|
682
|
-
if (token && accountID) {
|
|
683
|
-
logger.debug('Step 2 retry successful with region change');
|
|
684
|
-
setApiBaseUrl(originalUrl); // Restore original URL on success
|
|
685
|
-
return [true, token, accountID, countryCode || newCountryCode];
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
logger.error('Step 2 retry failed:', retryResponse);
|
|
690
|
-
setApiBaseUrl(originalUrl); // Restore original URL on failure
|
|
691
|
-
return [false, null, null, null];
|
|
692
|
-
} else {
|
|
693
|
-
logger.error('No region change token in cross-region error response');
|
|
694
|
-
}
|
|
658
|
+
logger.debug('Cross-region error detected in Step 2 - need to use different region');
|
|
659
|
+
// For cross-region errors, we need to retry the entire flow with a different region
|
|
660
|
+
// The authorization code from Step 1 is tied to the endpoint it was obtained from
|
|
661
|
+
setApiBaseUrl(originalUrl); // Restore original URL
|
|
662
|
+
return [false, null, null, 'cross_region_retry'];
|
|
695
663
|
}
|
|
696
664
|
|
|
697
665
|
logger.error('Step 2 error code:', loginResponse.code, 'message:', loginResponse.msg);
|
|
@@ -744,6 +712,13 @@ export class Helpers {
|
|
|
744
712
|
|
|
745
713
|
if (response.code && response.code !== 0) {
|
|
746
714
|
logger.error('Legacy login error code:', response.code, 'message:', response.msg);
|
|
715
|
+
|
|
716
|
+
// Check if it's a credential error
|
|
717
|
+
if (isCredentialError(response.code)) {
|
|
718
|
+
logger.error('Legacy auth: Credential error detected');
|
|
719
|
+
return [false, null, null, 'credential_error'];
|
|
720
|
+
}
|
|
721
|
+
|
|
747
722
|
return [false, null, null, null];
|
|
748
723
|
}
|
|
749
724
|
|
package/src/lib/vesync.ts
CHANGED
|
@@ -575,13 +575,8 @@ export class VeSync {
|
|
|
575
575
|
try {
|
|
576
576
|
logger.debug(`Authentication attempt ${attempt + 1} of ${retryAttempts}`);
|
|
577
577
|
|
|
578
|
-
//
|
|
579
|
-
|
|
580
|
-
if (detectedRegion !== this._region) {
|
|
581
|
-
logger.debug(`Adjusting region from ${this._region} to ${detectedRegion} based on email domain`);
|
|
582
|
-
this._region = detectedRegion;
|
|
583
|
-
setCurrentRegion(detectedRegion);
|
|
584
|
-
}
|
|
578
|
+
// Skip email-based region detection for now since we'll handle cross-region errors
|
|
579
|
+
// The cross-region error handling below will automatically switch to the correct region
|
|
585
580
|
|
|
586
581
|
// Try new authentication flow first with detected region
|
|
587
582
|
let [success, token, accountId, countryCode] = await Helpers.authNewFlow(this, this._appId, this._region);
|
|
@@ -592,15 +587,36 @@ export class VeSync {
|
|
|
592
587
|
}
|
|
593
588
|
|
|
594
589
|
if (!success) {
|
|
590
|
+
// Check if it's a credential error (no point retrying)
|
|
591
|
+
if (countryCode === 'credential_error') {
|
|
592
|
+
logger.error('Authentication failed due to invalid credentials');
|
|
593
|
+
return false; // Exit immediately, don't retry
|
|
594
|
+
}
|
|
595
|
+
|
|
595
596
|
// Check if we need to try a different region
|
|
596
|
-
if (countryCode === 'cross_region') {
|
|
597
|
+
if (countryCode === 'cross_region' || countryCode === 'cross_region_retry') {
|
|
597
598
|
logger.debug('Cross-region error detected, trying opposite region...');
|
|
598
599
|
const alternateRegion = this._region === 'US' ? 'EU' : 'US';
|
|
600
|
+
logger.debug(`Switching from ${this._region} to ${alternateRegion} region`);
|
|
601
|
+
|
|
599
602
|
this._region = alternateRegion;
|
|
600
603
|
setCurrentRegion(alternateRegion);
|
|
604
|
+
|
|
605
|
+
// Update the API endpoint for the new region
|
|
606
|
+
const regionEndpoints: Record<string, string> = {
|
|
607
|
+
'US': 'https://smartapi.vesync.com',
|
|
608
|
+
'EU': 'https://smartapi.vesync.eu'
|
|
609
|
+
};
|
|
610
|
+
if (alternateRegion in regionEndpoints) {
|
|
611
|
+
setApiBaseUrl(regionEndpoints[alternateRegion]);
|
|
612
|
+
logger.debug(`API endpoint set to ${regionEndpoints[alternateRegion]}`);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Retry with the alternate region
|
|
601
616
|
[success, token, accountId, countryCode] = await Helpers.authNewFlow(this, this._appId, alternateRegion);
|
|
602
617
|
if (success) {
|
|
603
618
|
this.authFlowUsed = 'new';
|
|
619
|
+
logger.debug(`Authentication successful with ${alternateRegion} region`);
|
|
604
620
|
}
|
|
605
621
|
}
|
|
606
622
|
|
|
@@ -610,6 +626,10 @@ export class VeSync {
|
|
|
610
626
|
[success, token, accountId, countryCode] = await Helpers.authLegacyFlow(this);
|
|
611
627
|
if (success) {
|
|
612
628
|
this.authFlowUsed = 'legacy';
|
|
629
|
+
} else if (countryCode === 'credential_error') {
|
|
630
|
+
// Legacy auth also detected bad credentials
|
|
631
|
+
logger.error('Both authentication methods report invalid credentials');
|
|
632
|
+
return false; // Exit immediately
|
|
613
633
|
}
|
|
614
634
|
}
|
|
615
635
|
}
|