@gooin/garmin-connect 1.7.6 → 1.8.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/common/FileMFASessionStorage.js +1 -247
- package/dist/common/HttpClient copy.js +1 -361
- package/dist/common/HttpClient.d.ts +1 -0
- package/dist/common/HttpClient.js +1 -612
- package/dist/common/HttpClient.js.map +1 -1
- package/dist/common/HttpClientOLD.js +1 -361
- package/dist/common/HttpClientOLDV1.js +1 -361
- package/dist/common/HttpClientV1.js +1 -577
- package/dist/common/MFAManager.js +1 -126
- package/dist/common/MFASessionStorage.js +1 -3
- package/dist/common/RedisMFASessionStorage.js +1 -281
- package/dist/common/RedisMFASessionStorage.js.map +1 -1
- package/dist/garmin/GarminConnect.d.ts +15 -78
- package/dist/garmin/GarminConnect.js +1 -592
- package/dist/garmin/GarminConnect.js.map +1 -1
- package/dist/garmin/UrlClass.d.ts +21 -0
- package/dist/garmin/UrlClass.js +1 -149
- package/dist/garmin/UrlClass.js.map +1 -1
- package/dist/garmin/common/DateUtils.d.ts +1 -0
- package/dist/garmin/common/DateUtils.js +1 -38
- package/dist/garmin/common/DateUtils.js.map +1 -1
- package/dist/garmin/common/HydrationUtils.js +1 -16
- package/dist/garmin/common/WeightUtils.js +1 -9
- package/dist/garmin/modules/activity/base.d.ts +39 -0
- package/dist/garmin/modules/activity/base.js +1 -0
- package/dist/garmin/modules/activity/base.js.map +1 -0
- package/dist/garmin/modules/activity/cycling.d.ts +16 -0
- package/dist/garmin/modules/activity/cycling.js +1 -0
- package/dist/garmin/modules/activity/cycling.js.map +1 -0
- package/dist/garmin/modules/activity/running.d.ts +15 -0
- package/dist/garmin/modules/activity/running.js +1 -0
- package/dist/garmin/modules/activity/running.js.map +1 -0
- package/dist/garmin/modules/activity/training-status.d.ts +13 -0
- package/dist/garmin/modules/activity/training-status.js +1 -0
- package/dist/garmin/modules/activity/training-status.js.map +1 -0
- package/dist/garmin/modules/coach/activity-summary.d.ts +20 -0
- package/dist/garmin/modules/coach/activity-summary.js +1 -0
- package/dist/garmin/modules/coach/activity-summary.js.map +1 -0
- package/dist/garmin/modules/coach/helpers.d.ts +157 -0
- package/dist/garmin/modules/coach/helpers.js +1 -0
- package/dist/garmin/modules/coach/helpers.js.map +1 -0
- package/dist/garmin/modules/coach/training-overview.d.ts +5 -0
- package/dist/garmin/modules/coach/training-overview.js +1 -0
- package/dist/garmin/modules/coach/training-overview.js.map +1 -0
- package/dist/garmin/modules/coach/wellness-overview.d.ts +5 -0
- package/dist/garmin/modules/coach/wellness-overview.js +1 -0
- package/dist/garmin/modules/coach/wellness-overview.js.map +1 -0
- package/dist/garmin/modules/coach/wellness-summary.d.ts +4 -0
- package/dist/garmin/modules/coach/wellness-summary.js +1 -0
- package/dist/garmin/modules/coach/wellness-summary.js.map +1 -0
- package/dist/garmin/modules/coach.d.ts +60 -0
- package/dist/garmin/modules/coach.js +1 -0
- package/dist/garmin/modules/coach.js.map +1 -0
- package/dist/garmin/modules/course.d.ts +27 -0
- package/dist/garmin/modules/course.js +1 -0
- package/dist/garmin/modules/course.js.map +1 -0
- package/dist/garmin/modules/device.d.ts +11 -0
- package/dist/garmin/modules/device.js +1 -0
- package/dist/garmin/modules/device.js.map +1 -0
- package/dist/garmin/modules/misc.d.ts +18 -0
- package/dist/garmin/modules/misc.js +1 -0
- package/dist/garmin/modules/misc.js.map +1 -0
- package/dist/garmin/modules/types.d.ts +10 -0
- package/dist/garmin/modules/types.js +1 -0
- package/dist/garmin/modules/types.js.map +1 -0
- package/dist/garmin/modules/user.d.ts +18 -0
- package/dist/garmin/modules/user.js +1 -0
- package/dist/garmin/modules/user.js.map +1 -0
- package/dist/garmin/modules/wellness/body-battery.d.ts +11 -0
- package/dist/garmin/modules/wellness/body-battery.js +1 -0
- package/dist/garmin/modules/wellness/body-battery.js.map +1 -0
- package/dist/garmin/modules/wellness/heart-rate.d.ts +11 -0
- package/dist/garmin/modules/wellness/heart-rate.js +1 -0
- package/dist/garmin/modules/wellness/heart-rate.js.map +1 -0
- package/dist/garmin/modules/wellness/hrv.d.ts +12 -0
- package/dist/garmin/modules/wellness/hrv.js +1 -0
- package/dist/garmin/modules/wellness/hrv.js.map +1 -0
- package/dist/garmin/modules/wellness/hydration.d.ts +12 -0
- package/dist/garmin/modules/wellness/hydration.js +1 -0
- package/dist/garmin/modules/wellness/hydration.js.map +1 -0
- package/dist/garmin/modules/wellness/sleep.d.ts +16 -0
- package/dist/garmin/modules/wellness/sleep.js +1 -0
- package/dist/garmin/modules/wellness/sleep.js.map +1 -0
- package/dist/garmin/modules/wellness/weight.d.ts +20 -0
- package/dist/garmin/modules/wellness/weight.js +1 -0
- package/dist/garmin/modules/wellness/weight.js.map +1 -0
- package/dist/garmin/modules/workout.d.ts +23 -0
- package/dist/garmin/modules/workout.js +1 -0
- package/dist/garmin/modules/workout.js.map +1 -0
- package/dist/garmin/types/activity-stats.d.ts +21 -0
- package/dist/garmin/types/activity-stats.js +1 -0
- package/dist/garmin/types/activity-stats.js.map +1 -0
- package/dist/garmin/types/activity.js +1 -27
- package/dist/garmin/types/body-battery.d.ts +7 -0
- package/dist/garmin/types/body-battery.js +1 -0
- package/dist/garmin/types/body-battery.js.map +1 -0
- package/dist/garmin/types/coach.d.ts +533 -0
- package/dist/garmin/types/coach.js +1 -0
- package/dist/garmin/types/coach.js.map +1 -0
- package/dist/garmin/types/course.d.ts +3 -11
- package/dist/garmin/types/course.js +1 -3
- package/dist/garmin/types/cycling.d.ts +70 -0
- package/dist/garmin/types/cycling.js +1 -0
- package/dist/garmin/types/cycling.js.map +1 -0
- package/dist/garmin/types/device.d.ts +32 -0
- package/dist/garmin/types/device.js +1 -0
- package/dist/garmin/types/device.js.map +1 -0
- package/dist/garmin/types/golf.d.ts +4 -4
- package/dist/garmin/types/golf.js +1 -3
- package/dist/garmin/types/golf.js.map +1 -1
- package/dist/garmin/types/heartrate.d.ts +3 -3
- package/dist/garmin/types/heartrate.js +1 -2
- package/dist/garmin/types/hrv.d.ts +3 -0
- package/dist/garmin/types/hrv.js +1 -3
- package/dist/garmin/types/hydration.d.ts +3 -3
- package/dist/garmin/types/hydration.js +1 -2
- package/dist/garmin/types/index.d.ts +15 -0
- package/dist/garmin/types/index.js +1 -36
- package/dist/garmin/types/index.js.map +1 -1
- package/dist/garmin/types/personal-info.js +1 -3
- package/dist/garmin/types/race-prediction.d.ts +50 -0
- package/dist/garmin/types/race-prediction.js +1 -0
- package/dist/garmin/types/race-prediction.js.map +1 -0
- package/dist/garmin/types/sleep.js +1 -3
- package/dist/garmin/types/training-status.js +1 -3
- package/dist/garmin/types/weight.d.ts +46 -4
- package/dist/garmin/types/weight.js +1 -3
- package/dist/garmin/workouts/Running.js +1 -47
- package/dist/garmin/workouts/templates/RunningTemplate.js +1 -78
- package/dist/index.js +1 -11
- package/dist/utils.js +1 -37
- package/package.json +4 -3
|
@@ -1,577 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.HttpClient = void 0;
|
|
7
|
-
const axios_1 = __importDefault(require("axios"));
|
|
8
|
-
const tough_cookie_1 = require("tough-cookie");
|
|
9
|
-
const form_data_1 = __importDefault(require("form-data"));
|
|
10
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
11
|
-
const luxon_1 = require("luxon");
|
|
12
|
-
const oauth_1_0a_1 = __importDefault(require("oauth-1.0a"));
|
|
13
|
-
const qs_1 = __importDefault(require("qs"));
|
|
14
|
-
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
15
|
-
const CSRF_RE = new RegExp('name="_csrf"\\s+value="(.+?)"');
|
|
16
|
-
const TICKET_RE = new RegExp('ticket=([^"]+)"');
|
|
17
|
-
const ACCOUNT_LOCKED_RE = new RegExp('var statuss*=s*"([^"]*)"');
|
|
18
|
-
const PAGE_TITLE_RE = new RegExp('<title>([^<]*)</title>');
|
|
19
|
-
const USER_AGENT_CONNECTMOBILE = 'com.garmin.android.apps.connectmobile';
|
|
20
|
-
const USER_AGENT_CONNECT_IOS = 'GCM-iOS-5.7.2.1';
|
|
21
|
-
const USER_AGENT_BROWSER = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36';
|
|
22
|
-
const USER_AGENT_BROWSER_MAC = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
|
|
23
|
-
const OAUTH_CONSUMER_URL = 'https://thegarth.s3.amazonaws.com/oauth_consumer.json';
|
|
24
|
-
const HTTP_STATUS = {
|
|
25
|
-
UNAUTHORIZED: 401
|
|
26
|
-
};
|
|
27
|
-
let tokenRefreshPromise = null;
|
|
28
|
-
let refreshSubscribers = [];
|
|
29
|
-
class HttpClient {
|
|
30
|
-
constructor(url, config) {
|
|
31
|
-
var _a, _b;
|
|
32
|
-
const jar = new tough_cookie_1.CookieJar();
|
|
33
|
-
this.url = url;
|
|
34
|
-
this.client = axios_1.default.create({
|
|
35
|
-
timeout: (_a = config === null || config === void 0 ? void 0 : config.timeout) !== null && _a !== void 0 ? _a : 5000,
|
|
36
|
-
timeoutErrorMessage: `Request Timeout: > ${(_b = config === null || config === void 0 ? void 0 : config.timeout) !== null && _b !== void 0 ? _b : 5000} ms`,
|
|
37
|
-
maxRedirects: 10,
|
|
38
|
-
validateStatus: function (status) {
|
|
39
|
-
return status >= 200 && status < 400;
|
|
40
|
-
},
|
|
41
|
-
withCredentials: true,
|
|
42
|
-
jar: jar,
|
|
43
|
-
});
|
|
44
|
-
this.config = config;
|
|
45
|
-
this.client.interceptors.response.use((response) => {
|
|
46
|
-
var _a, _b, _c, _d;
|
|
47
|
-
// 跟踪重定向过程
|
|
48
|
-
if (((_a = response.config.url) === null || _a === void 0 ? void 0 : _a.includes('signin')) ||
|
|
49
|
-
((_b = response.config.url) === null || _b === void 0 ? void 0 : _b.includes('verifyMFA'))) {
|
|
50
|
-
console.log('> 响应跟踪 - URL:', response.config.url);
|
|
51
|
-
console.log('响应跟踪 - 状态码:', response.status);
|
|
52
|
-
console.log('响应跟踪 - 最终URL:', ((_c = response.request) === null || _c === void 0 ? void 0 : _c.responseURL) || response.config.url);
|
|
53
|
-
console.log('响应跟踪 - 重定向次数:', ((_d = response.request) === null || _d === void 0 ? void 0 : _d.redirectCount) || 0);
|
|
54
|
-
// 检查是否有Location头
|
|
55
|
-
if (response.headers.location) {
|
|
56
|
-
console.log('响应跟踪 - Location头:', response.headers.location);
|
|
57
|
-
}
|
|
58
|
-
// 检查响应头中可能的重定向信息
|
|
59
|
-
if (response.status >= 300 && response.status < 400) {
|
|
60
|
-
console.log('响应跟踪 - 检测到重定向状态码:', response.status);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return response;
|
|
64
|
-
}, async (error) => {
|
|
65
|
-
var _a, _b, _c, _d;
|
|
66
|
-
if (axios_1.default.isAxiosError(error) &&
|
|
67
|
-
error.code === 'ECONNABORTED') {
|
|
68
|
-
throw new Error(error.message || 'Request Timeout');
|
|
69
|
-
}
|
|
70
|
-
const originalRequest = error.config;
|
|
71
|
-
if (((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) === HTTP_STATUS.UNAUTHORIZED &&
|
|
72
|
-
!(originalRequest === null || originalRequest === void 0 ? void 0 : originalRequest._retry)) {
|
|
73
|
-
if (!this.oauth2Token) {
|
|
74
|
-
throw new Error('No OAuth2 token available');
|
|
75
|
-
}
|
|
76
|
-
originalRequest._retry = true;
|
|
77
|
-
try {
|
|
78
|
-
if (!tokenRefreshPromise) {
|
|
79
|
-
tokenRefreshPromise =
|
|
80
|
-
this.refreshOauth2Token().finally(() => {
|
|
81
|
-
tokenRefreshPromise = null;
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
await tokenRefreshPromise;
|
|
85
|
-
originalRequest.headers.Authorization = `Bearer ${this.oauth2Token.access_token}`;
|
|
86
|
-
return this.client(originalRequest);
|
|
87
|
-
}
|
|
88
|
-
catch (err) {
|
|
89
|
-
console.error('Token refresh failed:', err);
|
|
90
|
-
throw err;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (axios_1.default.isAxiosError(error) && error.response) {
|
|
94
|
-
// 添加错误响应的调试信息
|
|
95
|
-
if (((_b = error.response.config.url) === null || _b === void 0 ? void 0 : _b.includes('signin')) ||
|
|
96
|
-
((_c = error.response.config.url) === null || _c === void 0 ? void 0 : _c.includes('verifyMFA'))) {
|
|
97
|
-
console.log('> HTTP Error响应跟踪 - URL:', error.response.config.url);
|
|
98
|
-
console.log('HTTP Error响应跟踪 - 状态码:', error.response.status);
|
|
99
|
-
console.log('HTTP Error响应跟踪 - 最终URL:', ((_d = error.response.request) === null || _d === void 0 ? void 0 : _d.responseURL) ||
|
|
100
|
-
error.response.config.url);
|
|
101
|
-
if (error.response.headers.location) {
|
|
102
|
-
console.log('HTTP Error响应跟踪 - Location头:', error.response.headers.location);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
this.handleError(error.response);
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
// 处理没有response的情况
|
|
109
|
-
throw new Error('Network error or unknown error occurred');
|
|
110
|
-
}
|
|
111
|
-
throw error;
|
|
112
|
-
});
|
|
113
|
-
this.client.interceptors.request.use(async (config) => {
|
|
114
|
-
if (this.oauth2Token) {
|
|
115
|
-
config.headers.Authorization =
|
|
116
|
-
'Bearer ' + this.oauth2Token.access_token;
|
|
117
|
-
}
|
|
118
|
-
return config;
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
async fetchOauthConsumer() {
|
|
122
|
-
const response = await axios_1.default.get(OAUTH_CONSUMER_URL);
|
|
123
|
-
this.OAUTH_CONSUMER = {
|
|
124
|
-
key: response.data.consumer_key,
|
|
125
|
-
secret: response.data.consumer_secret
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
async checkTokenVaild() {
|
|
129
|
-
if (this.oauth2Token) {
|
|
130
|
-
if (this.oauth2Token.expires_at < luxon_1.DateTime.now().toSeconds()) {
|
|
131
|
-
console.error('Token expired!');
|
|
132
|
-
await this.refreshOauth2Token();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
async get(url, config) {
|
|
137
|
-
const response = await this.client.get(url, config);
|
|
138
|
-
return response === null || response === void 0 ? void 0 : response.data;
|
|
139
|
-
}
|
|
140
|
-
async post(url, data, config) {
|
|
141
|
-
var _a, _b;
|
|
142
|
-
try {
|
|
143
|
-
const response = await this.client.post(url, data, config);
|
|
144
|
-
// 如果是MFA验证请求,添加额外的调试信息
|
|
145
|
-
if (url.includes('verifyMFA')) {
|
|
146
|
-
console.log('MFA验证响应状态码:', response.status);
|
|
147
|
-
console.log('MFA验证响应头 Location:', response.headers.location);
|
|
148
|
-
console.log('MFA验证响应头 Set-Cookie:', response.headers['set-cookie']);
|
|
149
|
-
console.log('MFA验证最终URL:', ((_a = response.request) === null || _a === void 0 ? void 0 : _a.responseURL) || response.config.url);
|
|
150
|
-
// 检查是否重定向到了包含logintoken的URL
|
|
151
|
-
if (((_b = response.request) === null || _b === void 0 ? void 0 : _b.responseURL) &&
|
|
152
|
-
response.request.responseURL.includes('logintoken')) {
|
|
153
|
-
console.log('检测到重定向到包含logintoken的URL:', response.request.responseURL);
|
|
154
|
-
// 如果重定向到了包含logintoken的URL,我们需要获取重定向后的页面内容
|
|
155
|
-
// 因为Axios可能没有自动跟随重定向,我们需要手动获取
|
|
156
|
-
if (!response.data.includes('Success') &&
|
|
157
|
-
!response.data.includes('ticket=')) {
|
|
158
|
-
console.log('响应数据不包含成功标识,手动获取重定向后的页面内容');
|
|
159
|
-
const redirectResponse = await this.client.get(response.request.responseURL, {
|
|
160
|
-
headers: {
|
|
161
|
-
'User-Agent': USER_AGENT_CONNECT_IOS
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
console.log('手动获取重定向页面状态码:', redirectResponse.status);
|
|
165
|
-
console.log('手动获取重定向页面长度:', redirectResponse.data.length);
|
|
166
|
-
return redirectResponse.data;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return response.data;
|
|
171
|
-
}
|
|
172
|
-
catch (error) {
|
|
173
|
-
console.error('POST请求失败:', error);
|
|
174
|
-
throw error;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
async put(url, data, config) {
|
|
178
|
-
const response = await this.client.put(url, data, config);
|
|
179
|
-
return response === null || response === void 0 ? void 0 : response.data;
|
|
180
|
-
}
|
|
181
|
-
async delete(url, config) {
|
|
182
|
-
const response = await this.client.post(url, null, {
|
|
183
|
-
...config,
|
|
184
|
-
headers: {
|
|
185
|
-
...config === null || config === void 0 ? void 0 : config.headers,
|
|
186
|
-
'X-Http-Method-Override': 'DELETE'
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
return response === null || response === void 0 ? void 0 : response.data;
|
|
190
|
-
}
|
|
191
|
-
setCommonHeader(headers) {
|
|
192
|
-
lodash_1.default.each(headers, (headerValue, key) => {
|
|
193
|
-
this.client.defaults.headers.common[key] = headerValue;
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
handleError(response) {
|
|
197
|
-
this.handleHttpError(response);
|
|
198
|
-
}
|
|
199
|
-
handleHttpError(response) {
|
|
200
|
-
const { status, statusText, data } = response;
|
|
201
|
-
const errorMessage = {
|
|
202
|
-
status,
|
|
203
|
-
statusText,
|
|
204
|
-
data: typeof data === 'object' ? JSON.stringify(data) : data
|
|
205
|
-
};
|
|
206
|
-
console.error('HTTP Error:', errorMessage);
|
|
207
|
-
throw new Error(`HTTP Error (${status}): ${statusText}`);
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Login to Garmin Connect
|
|
211
|
-
* @param username
|
|
212
|
-
* @param password
|
|
213
|
-
* @param mfaCallback Optional MFA callback function
|
|
214
|
-
* @returns {Promise<{oauth1: IOauth1; oauth2: IOauth2Token}>}
|
|
215
|
-
*/
|
|
216
|
-
async login(username, password, mfaCallback) {
|
|
217
|
-
try {
|
|
218
|
-
// 获取OAuth Consumer信息
|
|
219
|
-
if (!this.OAUTH_CONSUMER) {
|
|
220
|
-
await this.fetchOauthConsumer();
|
|
221
|
-
}
|
|
222
|
-
// 定义参数,与Python版本保持一致
|
|
223
|
-
const SSO = this.url.GARMIN_SSO;
|
|
224
|
-
const SSO_EMBED = this.url.GARMIN_SSO_EMBED;
|
|
225
|
-
const SSO_EMBED_PARAMS = {
|
|
226
|
-
id: 'gauth-widget',
|
|
227
|
-
embedWidget: 'true',
|
|
228
|
-
gauthHost: SSO
|
|
229
|
-
};
|
|
230
|
-
const SIGNIN_PARAMS = {
|
|
231
|
-
id: 'gauth-widget',
|
|
232
|
-
embedWidget: true,
|
|
233
|
-
clientId: 'GarminConnect',
|
|
234
|
-
locale: 'en',
|
|
235
|
-
gauthHost: this.url.GARMIN_SSO_EMBED,
|
|
236
|
-
service: this.url.GARMIN_SSO_EMBED,
|
|
237
|
-
source: this.url.GARMIN_SSO_EMBED,
|
|
238
|
-
redirectAfterAccountLoginUrl: this.url.GARMIN_SSO_EMBED,
|
|
239
|
-
redirectAfterAccountCreationUrl: this.url.GARMIN_SSO_EMBED
|
|
240
|
-
};
|
|
241
|
-
// 设置cookies - 与Python版本一致
|
|
242
|
-
await this.get(`${SSO}/embed`, {
|
|
243
|
-
params: SSO_EMBED_PARAMS,
|
|
244
|
-
headers: {
|
|
245
|
-
'User-Agent': USER_AGENT_CONNECT_IOS
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
// 获取CSRF令牌 - 与Python版本一致
|
|
249
|
-
const signinResponse = await this.get(`${SSO}/signin`, {
|
|
250
|
-
params: SIGNIN_PARAMS,
|
|
251
|
-
headers: {
|
|
252
|
-
'User-Agent': USER_AGENT_CONNECT_IOS,
|
|
253
|
-
Referer: `${SSO}/embed`
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
// 保存登录页面HTML用于调试
|
|
257
|
-
this.saveHtmlToFile(signinResponse, 'signin_page');
|
|
258
|
-
const csrfToken = this.extractCsrfToken(signinResponse);
|
|
259
|
-
console.log('🚀 - login - csrfToken:', csrfToken);
|
|
260
|
-
if (!csrfToken) {
|
|
261
|
-
throw new Error('无法从登录页面提取CSRF令牌');
|
|
262
|
-
}
|
|
263
|
-
// 提交登录表单 - 使用FormData方式,与旧版本一致
|
|
264
|
-
const form = new form_data_1.default();
|
|
265
|
-
form.append('username', username);
|
|
266
|
-
form.append('password', password);
|
|
267
|
-
form.append('embed', 'true');
|
|
268
|
-
form.append('_csrf', csrfToken);
|
|
269
|
-
const loginResponse = await this.post(`${SSO}/signin`, form, {
|
|
270
|
-
params: SIGNIN_PARAMS,
|
|
271
|
-
headers: {
|
|
272
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
273
|
-
'Dnt': 1,
|
|
274
|
-
'Origin': this.url.GARMIN_SSO_ORIGIN,
|
|
275
|
-
'Referer': `${SSO}/signin`,
|
|
276
|
-
'User-Agent': USER_AGENT_CONNECT_IOS
|
|
277
|
-
},
|
|
278
|
-
maxRedirects: 10 // 确保跟随重定向
|
|
279
|
-
});
|
|
280
|
-
``;
|
|
281
|
-
// 保存登录响应HTML用于调试
|
|
282
|
-
this.saveHtmlToFile(loginResponse, 'login_response');
|
|
283
|
-
const title = this.extractPageTitle(loginResponse);
|
|
284
|
-
console.log('登录后页面标题:', title);
|
|
285
|
-
// 处理MFA - 与Python版本一致
|
|
286
|
-
// 使用与Python版本相同的关键词检测MFA需求
|
|
287
|
-
const mfaKeywords = ['MFA', 'Enter security code', 'Enter MFA code'];
|
|
288
|
-
const needsMfa = mfaKeywords.some(keyword => title && title.toLowerCase().includes(keyword.toLowerCase()));
|
|
289
|
-
let finalResponse = loginResponse; // 默认使用登录响应
|
|
290
|
-
if (needsMfa) {
|
|
291
|
-
if (!mfaCallback) {
|
|
292
|
-
throw new Error('需要MFA验证但未提供回调函数');
|
|
293
|
-
}
|
|
294
|
-
console.log('检测到需要MFA验证,页面标题:', title);
|
|
295
|
-
// 处理MFA验证
|
|
296
|
-
finalResponse = await this.handleMfaVerification(loginResponse, SIGNIN_PARAMS, mfaCallback);
|
|
297
|
-
// 检查MFA验证后的响应是否包含成功标识
|
|
298
|
-
const mfaResultTitle = this.extractPageTitle(finalResponse);
|
|
299
|
-
console.log('MFA验证后页面标题:', mfaResultTitle);
|
|
300
|
-
if (mfaResultTitle !== 'Success' && !finalResponse.includes('ticket=')) {
|
|
301
|
-
throw new Error(`MFA验证后未获得成功页面,当前页面: ${mfaResultTitle}`);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
else if (title !== 'Success') {
|
|
305
|
-
throw new Error(`登录失败,页面标题: ${title}`);
|
|
306
|
-
}
|
|
307
|
-
// 提取ticket - 从最终响应中提取
|
|
308
|
-
const ticket = this.extractTicket(finalResponse);
|
|
309
|
-
if (!ticket) {
|
|
310
|
-
throw new Error('无法从响应中提取ticket');
|
|
311
|
-
}
|
|
312
|
-
// 获取OAuth1令牌
|
|
313
|
-
const oauth1 = await this.getOauth1Token(ticket);
|
|
314
|
-
// 交换OAuth2令牌
|
|
315
|
-
await this.exchange(oauth1);
|
|
316
|
-
return { oauth1, oauth2: this.oauth2Token };
|
|
317
|
-
}
|
|
318
|
-
catch (error) {
|
|
319
|
-
console.error('登录失败:', error);
|
|
320
|
-
throw error;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* 保存HTML内容到文件,用于调试
|
|
325
|
-
* @param htmlContent HTML内容
|
|
326
|
-
* @param filename 文件名(不含扩展名)
|
|
327
|
-
*/
|
|
328
|
-
saveHtmlToFile(htmlContent, filename) {
|
|
329
|
-
try {
|
|
330
|
-
const fs = require('fs');
|
|
331
|
-
const path = require('path');
|
|
332
|
-
const debugDir = path.join(process.cwd(), 'debug');
|
|
333
|
-
// 确保调试目录存在
|
|
334
|
-
if (!fs.existsSync(debugDir)) {
|
|
335
|
-
fs.mkdirSync(debugDir, { recursive: true });
|
|
336
|
-
}
|
|
337
|
-
const filePath = path.join(debugDir, `${filename}.html`);
|
|
338
|
-
fs.writeFileSync(filePath, htmlContent);
|
|
339
|
-
console.log(`已保存调试文件: ${filePath}`);
|
|
340
|
-
}
|
|
341
|
-
catch (error) {
|
|
342
|
-
console.error('保存调试文件失败:', error);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* 处理MFA验证
|
|
347
|
-
* @param loginResponse 登录响应HTML
|
|
348
|
-
* @param signinParams 登录参数
|
|
349
|
-
* @param mfaCallback MFA验证码回调函数
|
|
350
|
-
* @returns MFA验证响应
|
|
351
|
-
*/
|
|
352
|
-
async handleMfaVerification(loginResponse, signinParams, mfaCallback) {
|
|
353
|
-
try {
|
|
354
|
-
// 保存MFA页面HTML用于调试
|
|
355
|
-
this.saveHtmlToFile(loginResponse, 'mfa_page');
|
|
356
|
-
// 提取CSRF令牌
|
|
357
|
-
const csrfToken = this.extractCsrfToken(loginResponse);
|
|
358
|
-
if (!csrfToken) {
|
|
359
|
-
throw new Error('无法从MFA页面提取CSRF令牌');
|
|
360
|
-
}
|
|
361
|
-
// 获取MFA验证码
|
|
362
|
-
const mfaCode = await mfaCallback();
|
|
363
|
-
if (!mfaCode) {
|
|
364
|
-
throw new Error('未提供MFA验证码');
|
|
365
|
-
}
|
|
366
|
-
// 处理MFA验证 - 使用FormData方式,与旧版本一致
|
|
367
|
-
const SSO = this.url.GARMIN_SSO;
|
|
368
|
-
const mfaForm = new form_data_1.default();
|
|
369
|
-
mfaForm.append('mfa-code', mfaCode);
|
|
370
|
-
mfaForm.append('embed', 'true');
|
|
371
|
-
mfaForm.append('_csrf', csrfToken);
|
|
372
|
-
const mfaResult = await this.post(`${SSO}/mfa/verify`, mfaForm, {
|
|
373
|
-
params: signinParams,
|
|
374
|
-
headers: {
|
|
375
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
376
|
-
'Dnt': 1,
|
|
377
|
-
'Origin': this.url.GARMIN_SSO_ORIGIN,
|
|
378
|
-
'Referer': `${SSO}/signin`,
|
|
379
|
-
'User-Agent': USER_AGENT_BROWSER
|
|
380
|
-
},
|
|
381
|
-
maxRedirects: 10,
|
|
382
|
-
// 添加响应拦截器,确保获取重定向后的最终响应
|
|
383
|
-
transformResponse: [
|
|
384
|
-
function (data, headers) {
|
|
385
|
-
// 检查是否有重定向
|
|
386
|
-
if (headers.location &&
|
|
387
|
-
headers.location.includes('logintoken')) {
|
|
388
|
-
console.log('检测到重定向到包含logintoken的URL:', headers.location);
|
|
389
|
-
}
|
|
390
|
-
return data;
|
|
391
|
-
}
|
|
392
|
-
]
|
|
393
|
-
});
|
|
394
|
-
console.log('MFA验证完成,响应长度:', mfaResult.length);
|
|
395
|
-
// 保存MFA验证响应用于调试
|
|
396
|
-
this.saveHtmlToFile(mfaResult, 'mfa_verification_response');
|
|
397
|
-
// 检查MFA验证后的页面标题
|
|
398
|
-
const mfaResultTitle = this.extractPageTitle(mfaResult);
|
|
399
|
-
console.log('MFA验证后的页面标题:', mfaResultTitle);
|
|
400
|
-
// 验证MFA是否成功
|
|
401
|
-
if (mfaResultTitle !== 'Success' &&
|
|
402
|
-
!mfaResult.includes('ticket=')) {
|
|
403
|
-
throw new Error(`MFA验证失败,页面标题: ${mfaResultTitle}`);
|
|
404
|
-
}
|
|
405
|
-
// 如果MFA验证成功,更新loginResponse为MFA验证结果
|
|
406
|
-
// 这样后续的ticket提取将从MFA验证后的响应中进行
|
|
407
|
-
// 注意:这里我们不能直接修改loginResponse参数,但可以在返回值中提供MFA验证结果
|
|
408
|
-
return mfaResult;
|
|
409
|
-
}
|
|
410
|
-
catch (error) {
|
|
411
|
-
console.error('MFA验证失败:', error);
|
|
412
|
-
throw error;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* 从HTML中提取CSRF令牌
|
|
417
|
-
* @param html HTML字符串
|
|
418
|
-
* @returns CSRF令牌或null
|
|
419
|
-
*/
|
|
420
|
-
extractCsrfToken(html) {
|
|
421
|
-
const match = CSRF_RE.exec(html);
|
|
422
|
-
return match ? match[1] : null;
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* 从HTML中提取页面标题
|
|
426
|
-
* @param html HTML字符串
|
|
427
|
-
* @returns 页面标题或空字符串
|
|
428
|
-
*/
|
|
429
|
-
extractPageTitle(html) {
|
|
430
|
-
const match = PAGE_TITLE_RE.exec(html);
|
|
431
|
-
return match ? match[1] : '';
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* 从HTML中提取ticket
|
|
435
|
-
* @param html HTML字符串
|
|
436
|
-
* @returns ticket或null
|
|
437
|
-
*/
|
|
438
|
-
extractTicket(html) {
|
|
439
|
-
const match = TICKET_RE.exec(html);
|
|
440
|
-
return match ? match[1] : null;
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* 处理页面标题
|
|
444
|
-
* @param htmlStr HTML字符串
|
|
445
|
-
* @returns 页面标题
|
|
446
|
-
*/
|
|
447
|
-
handlePageTitle(htmlStr) {
|
|
448
|
-
const pageTitileRegResult = PAGE_TITLE_RE.exec(htmlStr);
|
|
449
|
-
if (pageTitileRegResult) {
|
|
450
|
-
const title = pageTitileRegResult[1];
|
|
451
|
-
console.log('login page title:', title);
|
|
452
|
-
if (lodash_1.default.includes(title, 'Update Phone Number')) {
|
|
453
|
-
// current I don't know where to update it
|
|
454
|
-
// See: https://github.com/matin/garth/issues/19
|
|
455
|
-
throw new Error('login failed (Update Phone number), please update your phone number, See: https://github.com/matin/garth/issues/19');
|
|
456
|
-
}
|
|
457
|
-
return title;
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
return '';
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
handleAccountLocked(htmlStr) {
|
|
464
|
-
const accountLockedRegResult = ACCOUNT_LOCKED_RE.exec(htmlStr);
|
|
465
|
-
if (accountLockedRegResult) {
|
|
466
|
-
const msg = accountLockedRegResult[1];
|
|
467
|
-
console.error(msg);
|
|
468
|
-
throw new Error('login failed (AccountLocked), please open connect web page to unlock your account');
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
async refreshOauth2Token() {
|
|
472
|
-
try {
|
|
473
|
-
if (!this.OAUTH_CONSUMER) {
|
|
474
|
-
await this.fetchOauthConsumer();
|
|
475
|
-
}
|
|
476
|
-
if (!this.oauth2Token || !this.oauth1Token) {
|
|
477
|
-
throw new Error('Missing required tokens for refresh');
|
|
478
|
-
}
|
|
479
|
-
const oauth1 = {
|
|
480
|
-
oauth: this.getOauthClient(this.OAUTH_CONSUMER),
|
|
481
|
-
token: this.oauth1Token
|
|
482
|
-
};
|
|
483
|
-
await this.exchange(oauth1);
|
|
484
|
-
console.log(`「${this.config.username}」in「${this.url.domain}」 OAuth2 token refreshed successfully`);
|
|
485
|
-
}
|
|
486
|
-
catch (error) {
|
|
487
|
-
console.error('Failed to refresh OAuth2 token:', error);
|
|
488
|
-
throw error;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
async getOauth1Token(ticket) {
|
|
492
|
-
if (!this.OAUTH_CONSUMER) {
|
|
493
|
-
throw new Error('No OAUTH_CONSUMER');
|
|
494
|
-
}
|
|
495
|
-
const params = {
|
|
496
|
-
ticket,
|
|
497
|
-
'login-url': this.url.GARMIN_SSO_EMBED,
|
|
498
|
-
'accepts-mfa-tokens': true
|
|
499
|
-
};
|
|
500
|
-
const url = `${this.url.OAUTH_URL}/preauthorized?${qs_1.default.stringify(params)}`;
|
|
501
|
-
const oauth = this.getOauthClient(this.OAUTH_CONSUMER);
|
|
502
|
-
const step4RequestData = {
|
|
503
|
-
url: url,
|
|
504
|
-
method: 'GET'
|
|
505
|
-
};
|
|
506
|
-
const headers = oauth.toHeader(oauth.authorize(step4RequestData));
|
|
507
|
-
// console.log('getOauth1Token - headers:', headers);
|
|
508
|
-
const response = await this.get(url, {
|
|
509
|
-
headers: {
|
|
510
|
-
...headers,
|
|
511
|
-
'User-Agent': USER_AGENT_CONNECTMOBILE
|
|
512
|
-
}
|
|
513
|
-
});
|
|
514
|
-
// console.log('getOauth1Token - response:', response);
|
|
515
|
-
const token = qs_1.default.parse(response);
|
|
516
|
-
// console.log('getOauth1Token - token:', token);
|
|
517
|
-
this.oauth1Token = token;
|
|
518
|
-
return { token, oauth };
|
|
519
|
-
}
|
|
520
|
-
getOauthClient(consumer) {
|
|
521
|
-
const oauth = new oauth_1_0a_1.default({
|
|
522
|
-
consumer: consumer,
|
|
523
|
-
signature_method: 'HMAC-SHA1',
|
|
524
|
-
hash_function(base_string, key) {
|
|
525
|
-
return node_crypto_1.default
|
|
526
|
-
.createHmac('sha1', key)
|
|
527
|
-
.update(base_string)
|
|
528
|
-
.digest('base64');
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
return oauth;
|
|
532
|
-
}
|
|
533
|
-
//
|
|
534
|
-
async exchange(oauth1) {
|
|
535
|
-
const token = {
|
|
536
|
-
key: oauth1.token.oauth_token,
|
|
537
|
-
secret: oauth1.token.oauth_token_secret
|
|
538
|
-
};
|
|
539
|
-
// console.log('exchange - token:', token);
|
|
540
|
-
const baseUrl = `${this.url.OAUTH_URL}/exchange/user/2.0`;
|
|
541
|
-
const requestData = {
|
|
542
|
-
url: baseUrl,
|
|
543
|
-
method: 'POST',
|
|
544
|
-
data: null
|
|
545
|
-
};
|
|
546
|
-
const step5AuthData = oauth1.oauth.authorize(requestData, token);
|
|
547
|
-
// console.log('login - step5AuthData:', step5AuthData);
|
|
548
|
-
const url = `${baseUrl}?${qs_1.default.stringify(step5AuthData)}`;
|
|
549
|
-
// console.log('exchange - url:', url);
|
|
550
|
-
this.oauth2Token = undefined;
|
|
551
|
-
const response = await this.post(url, null, {
|
|
552
|
-
headers: {
|
|
553
|
-
'User-Agent': USER_AGENT_CONNECTMOBILE,
|
|
554
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
555
|
-
}
|
|
556
|
-
});
|
|
557
|
-
// console.log('exchange - response:', response);
|
|
558
|
-
this.oauth2Token = this.setOauth2TokenExpiresAt(response);
|
|
559
|
-
// console.log('exchange - oauth2Token:', this.oauth2Token);
|
|
560
|
-
}
|
|
561
|
-
setOauth2TokenExpiresAt(token) {
|
|
562
|
-
const now = luxon_1.DateTime.now();
|
|
563
|
-
const expiresAt = now.plus({ seconds: token.expires_in });
|
|
564
|
-
const refreshTokenExpiresAt = now.plus({
|
|
565
|
-
seconds: token.refresh_token_expires_in
|
|
566
|
-
});
|
|
567
|
-
return {
|
|
568
|
-
...token,
|
|
569
|
-
last_update_date: now.toLocal().toString(),
|
|
570
|
-
expires_date: expiresAt.toLocal().toString(),
|
|
571
|
-
expires_at: expiresAt.toSeconds(),
|
|
572
|
-
refresh_token_expires_at: refreshTokenExpiresAt.toSeconds()
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
exports.HttpClient = HttpClient;
|
|
577
|
-
//# sourceMappingURL=HttpClientV1.js.map
|
|
1
|
+
'use strict';const a5_0x2e075f=a5_0xa90e;(function(_0x5169e6,_0x4dbd2d){const _0x256573=a5_0xa90e,_0x3204db=_0x5169e6();while(!![]){try{const _0x4da29f=parseInt(_0x256573(0x13d))/0x1*(-parseInt(_0x256573(0x12c))/0x2)+parseInt(_0x256573(0x16c))/0x3*(-parseInt(_0x256573(0x1aa))/0x4)+parseInt(_0x256573(0x112))/0x5*(-parseInt(_0x256573(0x11b))/0x6)+-parseInt(_0x256573(0x182))/0x7*(-parseInt(_0x256573(0x105))/0x8)+parseInt(_0x256573(0x176))/0x9+parseInt(_0x256573(0x181))/0xa+parseInt(_0x256573(0x16a))/0xb;if(_0x4da29f===_0x4dbd2d)break;else _0x3204db['push'](_0x3204db['shift']());}catch(_0x230dea){_0x3204db['push'](_0x3204db['shift']());}}}(a5_0x76da,0xa3e23));function a5_0x163b(_0x1a6f3f,_0x814b13){_0x1a6f3f=_0x1a6f3f-0x1b7;const _0x45dfef=a5_0x371a();let _0xd8c1ab=_0x45dfef[_0x1a6f3f];return _0xd8c1ab;}const a5_0x4ee2c5=a5_0x163b;(function(_0x344607,_0x5ab692){const _0x1ce188=a5_0xa90e,_0x561d86=a5_0x163b,_0x2d933d=_0x344607();while(!![]){try{const _0x176c91=-parseInt(_0x561d86(0x269))/0x1*(parseInt(_0x561d86(0x1e4))/0x2)+-parseInt(_0x561d86(0x1d6))/0x3+-parseInt(_0x561d86(0x242))/0x4*(-parseInt(_0x561d86(0x234))/0x5)+parseInt(_0x561d86(0x20a))/0x6*(-parseInt(_0x561d86(0x220))/0x7)+-parseInt(_0x561d86(0x245))/0x8*(-parseInt(_0x561d86(0x253))/0x9)+-parseInt(_0x561d86(0x232))/0xa+parseInt(_0x561d86(0x25f))/0xb;if(_0x176c91===_0x5ab692)break;else _0x2d933d[_0x1ce188(0x158)](_0x2d933d[_0x1ce188(0x163)]());}catch(_0x111a6e){_0x2d933d[_0x1ce188(0x158)](_0x2d933d[_0x1ce188(0x163)]());}}}(a5_0x371a,0xcacb5));function a5_0xa90e(_0x5b7a95,_0x545982){_0x5b7a95=_0x5b7a95-0x100;const _0x76dafb=a5_0x76da();let _0xa90ef3=_0x76dafb[_0x5b7a95];return _0xa90ef3;}var __importDefault=this&&this[a5_0x4ee2c5(0x20e)]||function(_0x2c769b){const _0x2743d8=a5_0x4ee2c5;return _0x2c769b&&_0x2c769b[_0x2743d8(0x1cb)]?_0x2c769b:{'default':_0x2c769b};};Object[a5_0x2e075f(0x186)](exports,a5_0x4ee2c5(0x1cb),{'value':!![]}),exports[a5_0x4ee2c5(0x21f)]=void 0x0;const axios_1=__importDefault(require(a5_0x2e075f(0x1a4))),tough_cookie_1=require(a5_0x4ee2c5(0x1f4)),form_data_1=__importDefault(require(a5_0x4ee2c5(0x22e))),lodash_1=__importDefault(require(a5_0x4ee2c5(0x1c9))),luxon_1=require(a5_0x4ee2c5(0x25d)),oauth_1_0a_1=__importDefault(require(a5_0x4ee2c5(0x1e9))),qs_1=__importDefault(require('qs')),node_crypto_1=__importDefault(require(a5_0x4ee2c5(0x247))),CSRF_RE=new RegExp(a5_0x4ee2c5(0x1e6)),TICKET_RE=new RegExp(a5_0x4ee2c5(0x1d3)),ACCOUNT_LOCKED_RE=new RegExp(a5_0x4ee2c5(0x238)),PAGE_TITLE_RE=new RegExp(a5_0x4ee2c5(0x21a)),USER_AGENT_CONNECTMOBILE=a5_0x4ee2c5(0x1d2),USER_AGENT_CONNECT_IOS=a5_0x4ee2c5(0x261),USER_AGENT_BROWSER=a5_0x2e075f(0x126),USER_AGENT_BROWSER_MAC=a5_0x4ee2c5(0x255),OAUTH_CONSUMER_URL=a5_0x2e075f(0x184),HTTP_STATUS={'UNAUTHORIZED':0x191};function a5_0x371a(){const _0x148aba=a5_0x2e075f,_0x2554d3=[_0x148aba(0x180),_0x148aba(0x1af),_0x148aba(0x14d),_0x148aba(0x14c),_0x148aba(0x114),_0x148aba(0x18a),_0x148aba(0x17d),_0x148aba(0x108),_0x148aba(0x190),_0x148aba(0x135),_0x148aba(0x18e),'some',_0x148aba(0x13e),_0x148aba(0x100),_0x148aba(0x149),_0x148aba(0x10b),'OAUTH_CONSUMER',_0x148aba(0x109),'verifyMFA','HTTP\x20Error响应跟踪\x20-\x20最终URL:','978vcOcoR',_0x148aba(0x138),'MFA验证后页面标题:',_0x148aba(0x168),_0x148aba(0x15a),_0x148aba(0x195),_0x148aba(0x166),_0x148aba(0x1ae),'domain','Enter\x20MFA\x20code',_0x148aba(0x151),_0x148aba(0x141),'common','interceptors',_0x148aba(0x142),_0x148aba(0x113),'<title>([^<]*)</title>',_0x148aba(0x124),'responseURL',_0x148aba(0x101),_0x148aba(0x17e),_0x148aba(0x19c),'53249HbXLpD',_0x148aba(0x197),'consumer_secret','ECONNABORTED',_0x148aba(0x17a),'signin','fetchOauthConsumer',_0x148aba(0x19a),_0x148aba(0x119),_0x148aba(0x187),_0x148aba(0x10d),_0x148aba(0x10a),'Authorization',_0x148aba(0x18c),'form-data','登录失败,页面标题:\x20',_0x148aba(0x178),_0x148aba(0x150),'3687900mgLmWY','log',_0x148aba(0x19b),'exchange','toLocal','HTTP\x20Error\x20(',_0x148aba(0x1a2),'无法从MFA页面提取CSRF令牌',_0x148aba(0x129),'client','DELETE',_0x148aba(0x19e),_0x148aba(0x133),_0x148aba(0x146),'Missing\x20required\x20tokens\x20for\x20refresh',_0x148aba(0x12d),_0x148aba(0x194),_0x148aba(0x159),_0x148aba(0x139),_0x148aba(0x18f),_0x148aba(0x15c),_0x148aba(0x11f),_0x148aba(0x15d),_0x148aba(0x118),_0x148aba(0x153),_0x148aba(0x14e),'parse','username',_0x148aba(0x11c),_0x148aba(0x16f),_0x148aba(0x1a7),_0x148aba(0x128),_0x148aba(0x125),_0x148aba(0x106),_0x148aba(0x13f),'Mozilla/5.0\x20(Macintosh;\x20Intel\x20Mac\x20OS\x20X\x2010_15_7)\x20AppleWebKit/537.36\x20(KHTML,\x20like\x20Gecko)\x20Chrome/131.0.0.0\x20Safari/537.36',_0x148aba(0x155),_0x148aba(0x157),_0x148aba(0x147),'handleMfaVerification',_0x148aba(0x15f),_0x148aba(0x173),_0x148aba(0x116),_0x148aba(0x140),'extractCsrfToken','48505996VHWiVi',_0x148aba(0x19f),'GCM-iOS-5.7.2.1',_0x148aba(0x145),'update','data',_0x148aba(0x132),_0x148aba(0x193),_0x148aba(0x152),_0x148aba(0x196),'97fQGpya',_0x148aba(0x115),_0x148aba(0x172),'Update\x20Phone\x20Number',_0x148aba(0x177),_0x148aba(0x148),_0x148aba(0x17b),_0x148aba(0x18d),'响应跟踪\x20-\x20重定向次数:',_0x148aba(0x17c),_0x148aba(0x1a6),_0x148aba(0x16d),'finally','each','HTTP\x20Error响应跟踪\x20-\x20状态码:','.html',_0x148aba(0x13b),_0x148aba(0x120),'error',_0x148aba(0x14f),_0x148aba(0x175),_0x148aba(0x1b0),_0x148aba(0x14b),'__esModule','>\x20响应跟踪\x20-\x20URL:',_0x148aba(0x15b),'token',_0x148aba(0x137),_0x148aba(0x121),_0x148aba(0x15e),_0x148aba(0x179),_0x148aba(0x13c),_0x148aba(0x199),_0x148aba(0x11d),_0x148aba(0x10f),_0x148aba(0x110),_0x148aba(0x130),_0x148aba(0x102),'headers',_0x148aba(0x143),'Request\x20Timeout',_0x148aba(0x11e),_0x148aba(0x156),_0x148aba(0x192),_0x148aba(0x1ac),_0x148aba(0x16b),_0x148aba(0x171),_0x148aba(0x12a),_0x148aba(0x167),'GARMIN_SSO',_0x148aba(0x1a3),_0x148aba(0x1ab),_0x148aba(0x191),_0x148aba(0x160),_0x148aba(0x1a1),_0x148aba(0x170),'oauth',_0x148aba(0x183),_0x148aba(0x131),'GET',_0x148aba(0x12b),_0x148aba(0x123),_0x148aba(0x174),'检测到重定向到包含logintoken的URL:',_0x148aba(0x154),'response'];return a5_0x371a=function(){return _0x2554d3;},a5_0x371a();}let tokenRefreshPromise=null,refreshSubscribers=[];function a5_0x76da(){const _0x3c0047=['HttpClient','」\x20OAuth2\x20token\x20refreshed\x20successfully','true','status','verifyMFA','application/x-www-form-urlencoded','var\x20statuss*=s*\x22([^\x22]*)\x22','name=\x22_csrf\x22\x5cs+value=\x22(.+?)\x22','axios','Token\x20refresh\x20failed:','code','MFA验证后的页面标题:','consumer_key','OAUTH_CONSUMER','2824588SxbnGZ','OAUTH_URL','location','expires_in','/embed','MFA验证失败,页面标题:\x20','lodash','toLowerCase','mkdirSync','debug','login\x20failed\x20(AccountLocked),\x20please\x20open\x20connect\x20web\x20page\x20to\x20unlock\x20your\x20account','Authorization','1991896plNQvy','3018753GrnEWT','cwd','):\x20','exec','signin_page','手动获取重定向页面状态码:','响应跟踪\x20-\x20Location头:','DateTime','Network\x20error\x20or\x20unknown\x20error\x20occurred','4587951dxfFTq','oauth1Token','client','65tbUQrI','append','MFA验证最终URL:','timeout','path','extractTicket','_retry','POST请求失败:','fetchOauthConsumer','370446tWhIBq','gauth-widget','use','_csrf','node:crypto','expires_at','object','/exchange/user/2.0','base64','手动获取重定向页面长度:','login','Mozilla/5.0\x20(Windows\x20NT\x2010.0;\x20Win64;\x20x64)\x20AppleWebKit/537.36\x20(KHTML,\x20like\x20Gecko)\x20Chrome/117.0.0.0\x20Safari/537.36','password','mfa_page','HTTP\x20Error:','includes','redirectCount','598hIIDrj','url','log','Failed\x20to\x20refresh\x20OAuth2\x20token:','now','handleAccountLocked','响应跟踪\x20-\x20最终URL:','default','refresh_token_expires_in','GARMIN_SSO_EMBED','writeFileSync','未提供MFA验证码','mfa_verification_response','MFA验证失败:','MFA验证响应头\x20Location:','getOauthClient','ticket=([^\x22]+)\x22','1189kuVIgB','embed','saveHtmlToFile','luxon','extractPageTitle','oauth_token','toSeconds','>\x20HTTP\x20Error响应跟踪\x20-\x20URL:','join','已保存调试文件:\x20','getOauth1Token','/signin','setOauth2TokenExpiresAt','existsSync','MFA验证完成,响应长度:','config','Token\x20expired!','🚀\x20-\x20login\x20-\x20csrfToken:','logintoken','GARMIN_SSO_ORIGIN','login_response','响应跟踪\x20-\x20检测到重定向状态码:','保存调试文件失败:','tough-cookie','oauth2Token','toString','stringify','push','MFA验证后未获得成功页面,当前页面:\x20','__importDefault','defaults','access_token','authorize','ticket=','需要MFA验证但未提供回调函数','oauth-1.0a','MFA验证响应状态码:','No\x20OAUTH_CONSUMER','shift','login\x20failed\x20(Update\x20Phone\x20number),\x20please\x20update\x20your\x20phone\x20number,\x20See:\x20\x20https://github.com/matin/garth/issues/19','isAxiosError','handleHttpError','21092fYgiSG','digest','interceptors','5314496OYwCUo','checkTokenVaild','3rXsDBW','refreshOauth2Token','data','」in「','login\x20page\x20title:','Bearer\x20','No\x20OAuth2\x20token\x20available','Success','登录失败:','登录后页面标题:','10119888IWJTCo','无法从登录页面提取CSRF令牌','MFA验证响应头\x20Set-Cookie:','com.garmin.android.apps.connectmobile','handlePageTitle','sha1','message','get','toHeader','responseURL','Enter\x20security\x20code','6790030lhteYO','7YfGpgB','Request\x20Timeout:\x20>\x20','https://thegarth.s3.amazonaws.com/oauth_consumer.json','MFA','defineProperty','setCommonHeader','响应数据不包含成功标识,手动获取重定向后的页面内容','检测到需要MFA验证,页面标题:','handleError','error','oauth_token_secret','mfa-code','plus','8PLKXsr','CookieJar','put','UNAUTHORIZED','request','136lLqqAa','length','响应跟踪\x20-\x20状态码:','createHmac','create','post','\x20ms','36265qZwSFh'];a5_0x76da=function(){return _0x3c0047;};return a5_0x76da();}class HttpClient{constructor(_0x35ac61,_0x18b9c1){const _0x2729a9=a5_0x2e075f,_0x33c632=a5_0x4ee2c5;var _0x1c1b78,_0xcd4731;const _0x3cf4a8=new tough_cookie_1[(_0x33c632(0x1fe))]();this[_0x33c632(0x241)]=_0x35ac61,this[_0x33c632(0x23b)]=axios_1[_0x33c632(0x23e)][_0x2729a9(0x198)]({'timeout':(_0x1c1b78=_0x18b9c1===null||_0x18b9c1===void 0x0?void 0x0:_0x18b9c1[_0x33c632(0x26a)])!==null&&_0x1c1b78!==void 0x0?_0x1c1b78:0x1388,'timeoutErrorMessage':_0x33c632(0x1ed)+((_0xcd4731=_0x18b9c1===null||_0x18b9c1===void 0x0?void 0x0:_0x18b9c1[_0x33c632(0x26a)])!==null&&_0xcd4731!==void 0x0?_0xcd4731:0x1388)+_0x33c632(0x227),'maxRedirects':0xa,'validateStatus':function(_0x214198){return _0x214198>=0xc8&&_0x214198<0x190;},'withCredentials':!![],'jar':_0x3cf4a8}),this[_0x2729a9(0x14c)]=_0x18b9c1,this[_0x33c632(0x23b)][_0x33c632(0x217)][_0x33c632(0x1f5)][_0x33c632(0x1d5)](_0x2cea02=>{const _0x40472d=_0x2729a9,_0x4a39fb=_0x33c632;var _0x5623ca,_0x12ebf3,_0x33e265,_0xce7657;return(((_0x5623ca=_0x2cea02[_0x4a39fb(0x1f9)][_0x4a39fb(0x241)])===null||_0x5623ca===void 0x0?void 0x0:_0x5623ca[_0x4a39fb(0x1e3)](_0x4a39fb(0x225)))||((_0x12ebf3=_0x2cea02[_0x40472d(0x14c)][_0x4a39fb(0x241)])===null||_0x12ebf3===void 0x0?void 0x0:_0x12ebf3[_0x4a39fb(0x1e3)](_0x4a39fb(0x208))))&&(console[_0x4a39fb(0x233)](_0x4a39fb(0x1cc),_0x2cea02[_0x4a39fb(0x1f9)][_0x40472d(0x12d)]),console[_0x40472d(0x12e)](_0x4a39fb(0x268),_0x2cea02[_0x4a39fb(0x260)]),console[_0x4a39fb(0x233)](_0x4a39fb(0x265),((_0x33e265=_0x2cea02[_0x4a39fb(0x266)])===null||_0x33e265===void 0x0?void 0x0:_0x33e265[_0x4a39fb(0x21c)])||_0x2cea02['config'][_0x4a39fb(0x241)]),console[_0x4a39fb(0x233)](_0x4a39fb(0x1bc),((_0xce7657=_0x2cea02[_0x4a39fb(0x266)])===null||_0xce7657===void 0x0?void 0x0:_0xce7657[_0x4a39fb(0x1f0)])||0x0),_0x2cea02[_0x4a39fb(0x1da)][_0x4a39fb(0x1e0)]&&console['log'](_0x40472d(0x10c),_0x2cea02[_0x4a39fb(0x1da)][_0x4a39fb(0x1e0)]),_0x2cea02[_0x40472d(0x19f)]>=0x12c&&_0x2cea02[_0x4a39fb(0x260)]<0x190&&console[_0x4a39fb(0x233)](_0x4a39fb(0x267),_0x2cea02[_0x4a39fb(0x260)])),_0x2cea02;},async _0x4ddd70=>{const _0x183ade=_0x2729a9,_0x2c9c9a=_0x33c632;var _0x4b035d,_0x26ddfa,_0x34cfe3,_0x1c8d9;if(axios_1[_0x2c9c9a(0x23e)][_0x183ade(0x165)](_0x4ddd70)&&_0x4ddd70[_0x2c9c9a(0x1be)]===_0x2c9c9a(0x223))throw new Error(_0x4ddd70[_0x2c9c9a(0x1bd)]||_0x2c9c9a(0x1dc));const _0x473491=_0x4ddd70[_0x2c9c9a(0x1f9)];if(((_0x4b035d=_0x4ddd70===null||_0x4ddd70===void 0x0?void 0x0:_0x4ddd70[_0x2c9c9a(0x1f5)])===null||_0x4b035d===void 0x0?void 0x0:_0x4b035d[_0x2c9c9a(0x260)])===HTTP_STATUS[_0x2c9c9a(0x1df)]&&!(_0x473491===null||_0x473491===void 0x0?void 0x0:_0x473491[_0x2c9c9a(0x249)])){if(!this[_0x2c9c9a(0x256)])throw new Error(_0x2c9c9a(0x26b));_0x473491[_0x2c9c9a(0x249)]=!![];try{return!tokenRefreshPromise&&(tokenRefreshPromise=this[_0x183ade(0x16d)]()[_0x2c9c9a(0x1c0)](()=>{tokenRefreshPromise=null;})),await tokenRefreshPromise,_0x473491[_0x2c9c9a(0x1da)][_0x183ade(0x104)]=_0x2c9c9a(0x1e2)+this[_0x2c9c9a(0x256)]['access_token'],this[_0x2c9c9a(0x23b)](_0x473491);}catch(_0x245c2a){console[_0x2c9c9a(0x1c6)](_0x183ade(0x1a5),_0x245c2a);throw _0x245c2a;}}if(axios_1[_0x2c9c9a(0x23e)][_0x183ade(0x165)](_0x4ddd70)&&_0x4ddd70[_0x2c9c9a(0x1f5)])(((_0x26ddfa=_0x4ddd70[_0x2c9c9a(0x1f5)][_0x2c9c9a(0x1f9)][_0x2c9c9a(0x241)])===null||_0x26ddfa===void 0x0?void 0x0:_0x26ddfa[_0x183ade(0x12a)](_0x2c9c9a(0x225)))||((_0x34cfe3=_0x4ddd70[_0x2c9c9a(0x1f5)][_0x2c9c9a(0x1f9)][_0x2c9c9a(0x241)])===null||_0x34cfe3===void 0x0?void 0x0:_0x34cfe3[_0x2c9c9a(0x1e3)](_0x2c9c9a(0x208))))&&(console[_0x2c9c9a(0x233)](_0x183ade(0x144),_0x4ddd70[_0x2c9c9a(0x1f5)][_0x2c9c9a(0x1f9)][_0x2c9c9a(0x241)]),console[_0x2c9c9a(0x233)](_0x2c9c9a(0x1c2),_0x4ddd70[_0x2c9c9a(0x1f5)][_0x2c9c9a(0x260)]),console[_0x2c9c9a(0x233)](_0x2c9c9a(0x209),((_0x1c8d9=_0x4ddd70[_0x2c9c9a(0x1f5)][_0x2c9c9a(0x266)])===null||_0x1c8d9===void 0x0?void 0x0:_0x1c8d9[_0x183ade(0x17f)])||_0x4ddd70[_0x2c9c9a(0x1f5)][_0x2c9c9a(0x1f9)][_0x2c9c9a(0x241)]),_0x4ddd70[_0x2c9c9a(0x1f5)][_0x2c9c9a(0x1da)]['location']&&console[_0x2c9c9a(0x233)]('HTTP\x20Error响应跟踪\x20-\x20Location头:',_0x4ddd70[_0x2c9c9a(0x1f5)][_0x2c9c9a(0x1da)][_0x2c9c9a(0x1e0)])),this[_0x2c9c9a(0x1fb)](_0x4ddd70[_0x2c9c9a(0x1f5)]);else throw new Error(_0x183ade(0x10e));throw _0x4ddd70;}),this[_0x2729a9(0x111)][_0x2729a9(0x169)][_0x33c632(0x266)][_0x33c632(0x1d5)](async _0x156594=>{const _0x20dda2=_0x33c632;return this[_0x20dda2(0x256)]&&(_0x156594[_0x20dda2(0x1da)][_0x20dda2(0x22c)]='Bearer\x20'+this[_0x20dda2(0x256)][_0x20dda2(0x246)]),_0x156594;});}async[a5_0x4ee2c5(0x226)](){const _0x4ab594=a5_0x2e075f,_0xd73574=a5_0x4ee2c5,_0x2cc463=await axios_1[_0xd73574(0x23e)][_0x4ab594(0x17d)](OAUTH_CONSUMER_URL);this[_0xd73574(0x206)]={'key':_0x2cc463[_0xd73574(0x264)][_0x4ab594(0x1a8)],'secret':_0x2cc463[_0xd73574(0x264)][_0xd73574(0x222)]};}async[a5_0x4ee2c5(0x1e1)](){const _0x3369c7=a5_0x2e075f,_0x550b4e=a5_0x4ee2c5;this[_0x550b4e(0x256)]&&(this[_0x550b4e(0x256)][_0x550b4e(0x1c5)]<luxon_1[_0x3369c7(0x10d)][_0x550b4e(0x1d8)]()[_0x550b4e(0x1db)]()&&(console[_0x550b4e(0x1c6)](_0x550b4e(0x1f8)),await this[_0x550b4e(0x1bf)]()));}async[a5_0x4ee2c5(0x1fc)](_0x185832,_0x50235c){const _0x5bd3f9=a5_0x4ee2c5,_0x42c33b=await this[_0x5bd3f9(0x23b)][_0x5bd3f9(0x1fc)](_0x185832,_0x50235c);return _0x42c33b===null||_0x42c33b===void 0x0?void 0x0:_0x42c33b[_0x5bd3f9(0x264)];}async[a5_0x4ee2c5(0x1d4)](_0x5afbdb,_0x10343a,_0xe4fc6f){const _0x4465bc=a5_0x2e075f,_0x3e6e9e=a5_0x4ee2c5;var _0x5040ce,_0x5efc55;try{const _0x203859=await this['client'][_0x3e6e9e(0x1d4)](_0x5afbdb,_0x10343a,_0xe4fc6f);if(_0x5afbdb[_0x3e6e9e(0x1e3)](_0x4465bc(0x1a0))){console[_0x3e6e9e(0x233)](_0x4465bc(0x161),_0x203859[_0x4465bc(0x19f)]),console[_0x3e6e9e(0x233)](_0x4465bc(0x13a),_0x203859[_0x3e6e9e(0x1da)][_0x3e6e9e(0x1e0)]),console[_0x3e6e9e(0x233)](_0x3e6e9e(0x230),_0x203859[_0x3e6e9e(0x1da)]['set-cookie']),console[_0x4465bc(0x12e)](_0x3e6e9e(0x1fa),((_0x5040ce=_0x203859['request'])===null||_0x5040ce===void 0x0?void 0x0:_0x5040ce['responseURL'])||_0x203859['config'][_0x3e6e9e(0x241)]);if(((_0x5efc55=_0x203859[_0x3e6e9e(0x266)])===null||_0x5efc55===void 0x0?void 0x0:_0x5efc55['responseURL'])&&_0x203859[_0x3e6e9e(0x266)][_0x3e6e9e(0x21c)][_0x3e6e9e(0x1e3)](_0x3e6e9e(0x1c7))){console[_0x4465bc(0x12e)](_0x3e6e9e(0x1f3),_0x203859[_0x3e6e9e(0x266)][_0x4465bc(0x17f)]);if(!_0x203859[_0x3e6e9e(0x264)][_0x4465bc(0x12a)](_0x3e6e9e(0x25b))&&!_0x203859[_0x3e6e9e(0x264)][_0x3e6e9e(0x1e3)](_0x3e6e9e(0x1d1))){console[_0x3e6e9e(0x233)](_0x4465bc(0x188));const _0x4b1a46=await this[_0x4465bc(0x111)]['get'](_0x203859[_0x4465bc(0x193)][_0x3e6e9e(0x21c)],{'headers':{'User-Agent':USER_AGENT_CONNECT_IOS}});return console[_0x3e6e9e(0x233)](_0x3e6e9e(0x205),_0x4b1a46[_0x3e6e9e(0x260)]),console[_0x3e6e9e(0x233)](_0x3e6e9e(0x21b),_0x4b1a46[_0x3e6e9e(0x264)][_0x3e6e9e(0x20f)]),_0x4b1a46[_0x4465bc(0x16e)];}}}return _0x203859[_0x4465bc(0x16e)];}catch(_0x1ee68e){console[_0x4465bc(0x18b)](_0x3e6e9e(0x228),_0x1ee68e);throw _0x1ee68e;}}async[a5_0x4ee2c5(0x1e8)](_0x2f0c33,_0x1c1ff9,_0x4872c9){const _0x38f866=a5_0x4ee2c5,_0x21c7da=await this['client'][_0x38f866(0x1e8)](_0x2f0c33,_0x1c1ff9,_0x4872c9);return _0x21c7da===null||_0x21c7da===void 0x0?void 0x0:_0x21c7da[_0x38f866(0x264)];}async['delete'](_0x239db6,_0x1c753b){const _0x42d33c=a5_0x2e075f,_0x1d9bf3=a5_0x4ee2c5,_0x26ec06=await this[_0x42d33c(0x111)][_0x1d9bf3(0x1d4)](_0x239db6,null,{..._0x1c753b,'headers':{..._0x1c753b===null||_0x1c753b===void 0x0?void 0x0:_0x1c753b[_0x1d9bf3(0x1da)],'X-Http-Method-Override':_0x1d9bf3(0x23c)}});return _0x26ec06===null||_0x26ec06===void 0x0?void 0x0:_0x26ec06[_0x1d9bf3(0x264)];}[a5_0x4ee2c5(0x229)](_0x211a8a){const _0x9d1b06=a5_0x4ee2c5;lodash_1[_0x9d1b06(0x23e)][_0x9d1b06(0x1c1)](_0x211a8a,(_0x4ae405,_0xa32d5e)=>{const _0x330598=_0x9d1b06;this[_0x330598(0x23b)][_0x330598(0x1cd)][_0x330598(0x1da)][_0x330598(0x216)][_0xa32d5e]=_0x4ae405;});}['handleError'](_0x1477f8){this['handleHttpError'](_0x1477f8);}[a5_0x4ee2c5(0x210)](_0x5349be){const _0x38ae63=a5_0x4ee2c5,{status:_0x4a02a3,statusText:_0x13b0d9,data:_0x19d89c}=_0x5349be,_0x1e6a17={'status':_0x4a02a3,'statusText':_0x13b0d9,'data':typeof _0x19d89c===_0x38ae63(0x1d0)?JSON[_0x38ae63(0x257)](_0x19d89c):_0x19d89c};console[_0x38ae63(0x1c6)](_0x38ae63(0x23a),_0x1e6a17);throw new Error(_0x38ae63(0x237)+_0x4a02a3+_0x38ae63(0x1fd)+_0x13b0d9);}async[a5_0x4ee2c5(0x252)](_0x5bac5c,_0x47485f,_0xcb683e){const _0x36a0ce=a5_0x2e075f,_0x408009=a5_0x4ee2c5;try{!this[_0x408009(0x206)]&&await this[_0x36a0ce(0x11a)]();const _0x1ed2f2=this[_0x408009(0x241)][_0x408009(0x1e5)],_0x4fa2d0=this[_0x36a0ce(0x12d)]['GARMIN_SSO_EMBED'],_0x1c8257={'id':_0x408009(0x24e),'embedWidget':_0x36a0ce(0x19e),'gauthHost':_0x1ed2f2},_0x964c20={'id':_0x408009(0x24e),'embedWidget':!![],'clientId':'GarminConnect','locale':'en','gauthHost':this[_0x36a0ce(0x12d)][_0x408009(0x1ff)],'service':this[_0x408009(0x241)][_0x408009(0x1ff)],'source':this['url'][_0x36a0ce(0x135)],'redirectAfterAccountLoginUrl':this[_0x408009(0x241)][_0x408009(0x1ff)],'redirectAfterAccountCreationUrl':this[_0x408009(0x241)][_0x408009(0x1ff)]};await this[_0x408009(0x1fc)](_0x1ed2f2+_0x408009(0x211),{'params':_0x1c8257,'headers':{'User-Agent':USER_AGENT_CONNECT_IOS}});const _0x7a67be=await this[_0x408009(0x1fc)](_0x1ed2f2+_0x408009(0x1b9),{'params':_0x964c20,'headers':{'User-Agent':USER_AGENT_CONNECT_IOS,'Referer':_0x1ed2f2+_0x408009(0x211)}});this['saveHtmlToFile'](_0x7a67be,_0x408009(0x22b));const _0x46dbb0=this[_0x408009(0x25e)](_0x7a67be);console[_0x36a0ce(0x12e)](_0x408009(0x24b),_0x46dbb0);if(!_0x46dbb0)throw new Error(_0x408009(0x1b8));const _0x39fd80=new form_data_1[(_0x36a0ce(0x133))]();_0x39fd80[_0x408009(0x219)](_0x408009(0x24d),_0x5bac5c),_0x39fd80[_0x408009(0x219)](_0x36a0ce(0x127),_0x47485f),_0x39fd80[_0x408009(0x219)](_0x36a0ce(0x13e),_0x408009(0x23d)),_0x39fd80['append'](_0x408009(0x1dd),_0x46dbb0);const _0x32a1b3=await this[_0x408009(0x1d4)](_0x1ed2f2+_0x408009(0x1b9),_0x39fd80,{'params':_0x964c20,'headers':{'Content-Type':_0x408009(0x1ea),'Dnt':0x1,'Origin':this[_0x408009(0x241)]['GARMIN_SSO_ORIGIN'],'Referer':_0x1ed2f2+'/signin','User-Agent':USER_AGENT_CONNECT_IOS},'maxRedirects':0xa});'',this[_0x408009(0x254)](_0x32a1b3,_0x408009(0x214));const _0x1a58ac=this[_0x408009(0x215)](_0x32a1b3);console[_0x408009(0x233)](_0x408009(0x1c8),_0x1a58ac);const _0x1734ef=[_0x36a0ce(0x185),_0x408009(0x1f6),_0x408009(0x213)],_0xe4648=_0x1734ef[_0x408009(0x201)](_0x3dd3fd=>_0x1a58ac&&_0x1a58ac[_0x408009(0x203)]()[_0x408009(0x1e3)](_0x3dd3fd[_0x408009(0x203)]()));let _0x54c784=_0x32a1b3;if(_0xe4648){if(!_0xcb683e)throw new Error(_0x408009(0x25a));console[_0x408009(0x233)](_0x36a0ce(0x189),_0x1a58ac),_0x54c784=await this[_0x408009(0x259)](_0x32a1b3,_0x964c20,_0xcb683e);const _0x377407=this[_0x408009(0x215)](_0x54c784);console['log'](_0x408009(0x20c),_0x377407);if(_0x377407!==_0x36a0ce(0x173)&&!_0x54c784[_0x408009(0x1e3)](_0x408009(0x1d1)))throw new Error(_0x408009(0x243)+_0x377407);}else{if(_0x1a58ac!==_0x408009(0x25b))throw new Error(_0x408009(0x22f)+_0x1a58ac);}const _0x1887c8=this['extractTicket'](_0x54c784);if(!_0x1887c8)throw new Error('无法从响应中提取ticket');const _0x200c3b=await this[_0x408009(0x258)](_0x1887c8);return await this[_0x408009(0x235)](_0x200c3b),{'oauth1':_0x200c3b,'oauth2':this[_0x36a0ce(0x155)]};}catch(_0x3fceb6){console[_0x408009(0x1c6)](_0x408009(0x1f2),_0x3fceb6);throw _0x3fceb6;}}[a5_0x4ee2c5(0x254)](_0x16e847,_0xf5ecd5){const _0x361938=a5_0x2e075f,_0x4bc34b=a5_0x4ee2c5;try{const _0xea72b9=require('fs'),_0x2b1584=require(_0x4bc34b(0x25c)),_0x2aa1a7=_0x2b1584['join'](process[_0x361938(0x107)](),_0x4bc34b(0x1d9));!_0xea72b9[_0x361938(0x14a)](_0x2aa1a7)&&_0xea72b9[_0x4bc34b(0x21d)](_0x2aa1a7,{'recursive':!![]});const _0x42f713=_0x2b1584[_0x4bc34b(0x262)](_0x2aa1a7,_0xf5ecd5+_0x4bc34b(0x1c3));_0xea72b9[_0x361938(0x136)](_0x42f713,_0x16e847),console[_0x361938(0x12e)](_0x4bc34b(0x23f)+_0x42f713);}catch(_0x1bf3fc){console[_0x4bc34b(0x1c6)](_0x4bc34b(0x24a),_0x1bf3fc);}}async[a5_0x4ee2c5(0x259)](_0x4dfcd2,_0xcb0480,_0x462ea3){const _0x2ddee9=a5_0x2e075f,_0x49bf7a=a5_0x4ee2c5;try{this[_0x49bf7a(0x254)](_0x4dfcd2,_0x49bf7a(0x251));const _0x450714=this['extractCsrfToken'](_0x4dfcd2);if(!_0x450714)throw new Error(_0x49bf7a(0x239));const _0x2fabb8=await _0x462ea3();if(!_0x2fabb8)throw new Error(_0x49bf7a(0x1cf));const _0x2bddf4=this[_0x49bf7a(0x241)][_0x49bf7a(0x1e5)],_0x4a3c5f=new form_data_1[(_0x49bf7a(0x23e))]();_0x4a3c5f[_0x49bf7a(0x219)](_0x49bf7a(0x1bb),_0x2fabb8),_0x4a3c5f[_0x49bf7a(0x219)](_0x49bf7a(0x202),_0x49bf7a(0x23d)),_0x4a3c5f[_0x49bf7a(0x219)](_0x49bf7a(0x1dd),_0x450714);const _0x3bd012=await this[_0x49bf7a(0x1d4)](_0x2bddf4+'/mfa/verify',_0x4a3c5f,{'params':_0xcb0480,'headers':{'Content-Type':_0x49bf7a(0x1ea),'Dnt':0x1,'Origin':this[_0x2ddee9(0x12d)][_0x49bf7a(0x231)],'Referer':_0x2bddf4+_0x49bf7a(0x1b9),'User-Agent':USER_AGENT_BROWSER},'maxRedirects':0xa,'transformResponse':[function(_0x1ff81e,_0x587197){const _0x2cd011=_0x2ddee9,_0x119473=_0x49bf7a;return _0x587197[_0x119473(0x1e0)]&&_0x587197[_0x119473(0x1e0)][_0x2cd011(0x12a)](_0x119473(0x1c7))&&console[_0x119473(0x233)](_0x119473(0x1f3),_0x587197[_0x119473(0x1e0)]),_0x1ff81e;}]});console[_0x49bf7a(0x233)](_0x49bf7a(0x1ca),_0x3bd012[_0x2ddee9(0x195)]),this[_0x2ddee9(0x13f)](_0x3bd012,_0x49bf7a(0x20b));const _0xf1f80b=this[_0x49bf7a(0x215)](_0x3bd012);console[_0x49bf7a(0x233)](_0x49bf7a(0x250),_0xf1f80b);if(_0xf1f80b!=='Success'&&!_0x3bd012['includes'](_0x49bf7a(0x1d1)))throw new Error(_0x49bf7a(0x1f7)+_0xf1f80b);return _0x3bd012;}catch(_0x14b7d3){console[_0x49bf7a(0x1c6)](_0x49bf7a(0x244),_0x14b7d3);throw _0x14b7d3;}}['extractCsrfToken'](_0x27dfdf){const _0x459a5b=a5_0x4ee2c5,_0x41429d=CSRF_RE[_0x459a5b(0x207)](_0x27dfdf);return _0x41429d?_0x41429d[0x1]:null;}[a5_0x4ee2c5(0x215)](_0x25b0e7){const _0x1b9296=a5_0x4ee2c5,_0x114bcb=PAGE_TITLE_RE[_0x1b9296(0x207)](_0x25b0e7);return _0x114bcb?_0x114bcb[0x1]:'';}[a5_0x2e075f(0x117)](_0x28a2b0){const _0x1d3696=a5_0x4ee2c5,_0x36638f=TICKET_RE[_0x1d3696(0x207)](_0x28a2b0);return _0x36638f?_0x36638f[0x1]:null;}[a5_0x4ee2c5(0x224)](_0x21466e){const _0x11b59f=a5_0x2e075f,_0x59b26f=a5_0x4ee2c5,_0x2a1327=PAGE_TITLE_RE[_0x59b26f(0x207)](_0x21466e);if(_0x2a1327){const _0x335468=_0x2a1327[0x1];console[_0x59b26f(0x233)](_0x59b26f(0x1eb),_0x335468);if(lodash_1[_0x59b26f(0x23e)][_0x59b26f(0x1e3)](_0x335468,_0x59b26f(0x1b7)))throw new Error(_0x11b59f(0x164));return _0x335468;}else return'';}[a5_0x4ee2c5(0x1ee)](_0x33589a){const _0x178b97=a5_0x2e075f,_0x2332e4=a5_0x4ee2c5,_0x42a2c4=ACCOUNT_LOCKED_RE[_0x2332e4(0x207)](_0x33589a);if(_0x42a2c4){const _0x566cc7=_0x42a2c4[0x1];console[_0x2332e4(0x1c6)](_0x566cc7);throw new Error(_0x178b97(0x103));}}async[a5_0x4ee2c5(0x1bf)](){const _0x495edb=a5_0x2e075f,_0x5ac71a=a5_0x4ee2c5;try{!this[_0x5ac71a(0x206)]&&await this[_0x5ac71a(0x226)]();if(!this[_0x5ac71a(0x256)]||!this[_0x5ac71a(0x1d7)])throw new Error(_0x5ac71a(0x240));const _0x716486={'oauth':this[_0x5ac71a(0x1c4)](this[_0x5ac71a(0x206)]),'token':this[_0x5ac71a(0x1d7)]};await this[_0x5ac71a(0x235)](_0x716486),console[_0x5ac71a(0x233)]('「'+this[_0x5ac71a(0x1f9)][_0x5ac71a(0x24d)]+_0x5ac71a(0x24f)+this[_0x5ac71a(0x241)][_0x5ac71a(0x212)]+_0x495edb(0x19d));}catch(_0x3a9241){console[_0x5ac71a(0x1c6)](_0x495edb(0x12f),_0x3a9241);throw _0x3a9241;}}async[a5_0x2e075f(0x147)](_0x27583a){const _0x3bb7b7=a5_0x2e075f,_0xa4bcdd=a5_0x4ee2c5;if(!this[_0x3bb7b7(0x1a9)])throw new Error(_0x3bb7b7(0x162));const _0xa675a3={'ticket':_0x27583a,'login-url':this[_0xa4bcdd(0x241)][_0x3bb7b7(0x135)],'accepts-mfa-tokens':!![]},_0x14e922=this[_0xa4bcdd(0x241)][_0xa4bcdd(0x1e7)]+'/preauthorized?'+qs_1[_0xa4bcdd(0x23e)][_0xa4bcdd(0x257)](_0xa675a3),_0x3ad389=this[_0xa4bcdd(0x1c4)](this[_0x3bb7b7(0x1a9)]),_0x38f27a={'url':_0x14e922,'method':_0xa4bcdd(0x1ef)},_0x95bfdf=_0x3ad389[_0xa4bcdd(0x21e)](_0x3ad389[_0xa4bcdd(0x248)](_0x38f27a)),_0x1cefb4=await this[_0xa4bcdd(0x1fc)](_0x14e922,{'headers':{..._0x95bfdf,'User-Agent':USER_AGENT_CONNECTMOBILE}}),_0x133e94=qs_1[_0xa4bcdd(0x23e)][_0xa4bcdd(0x24c)](_0x1cefb4);return this[_0xa4bcdd(0x1d7)]=_0x133e94,{'token':_0x133e94,'oauth':_0x3ad389};}[a5_0x4ee2c5(0x1c4)](_0x3e5be9){const _0x3a6753=a5_0x4ee2c5,_0xea95da=new oauth_1_0a_1[(_0x3a6753(0x23e))]({'consumer':_0x3e5be9,'signature_method':'HMAC-SHA1','hash_function'(_0xed620e,_0x20b00c){const _0x4c014c=_0x3a6753;return node_crypto_1[_0x4c014c(0x23e)][_0x4c014c(0x221)](_0x4c014c(0x1ba),_0x20b00c)[_0x4c014c(0x263)](_0xed620e)[_0x4c014c(0x20d)](_0x4c014c(0x1f1));}});return _0xea95da;}async[a5_0x4ee2c5(0x235)](_0xab6e93){const _0x597cc1=a5_0x2e075f,_0x23f9db=a5_0x4ee2c5,_0x559574={'key':_0xab6e93[_0x23f9db(0x1ce)][_0x23f9db(0x218)],'secret':_0xab6e93[_0x23f9db(0x1ce)][_0x23f9db(0x22d)]},_0x2f6b3a=this['url'][_0x23f9db(0x1e7)]+_0x597cc1(0x122),_0x1dc9bd={'url':_0x2f6b3a,'method':'POST','data':null},_0x2c0ced=_0xab6e93[_0x23f9db(0x1ec)][_0x23f9db(0x248)](_0x1dc9bd,_0x559574),_0x164683=_0x2f6b3a+'?'+qs_1[_0x597cc1(0x133)][_0x23f9db(0x257)](_0x2c0ced);this[_0x597cc1(0x155)]=undefined;const _0x4a30eb=await this[_0x23f9db(0x1d4)](_0x164683,null,{'headers':{'User-Agent':USER_AGENT_CONNECTMOBILE,'Content-Type':_0x23f9db(0x1ea)}});this[_0x597cc1(0x155)]=this[_0x23f9db(0x204)](_0x4a30eb);}[a5_0x4ee2c5(0x204)](_0x2f8509){const _0x428bac=a5_0x2e075f,_0x155f0d=a5_0x4ee2c5,_0x212d18=luxon_1[_0x155f0d(0x22a)][_0x428bac(0x130)](),_0x2ff92f=_0x212d18[_0x155f0d(0x200)]({'seconds':_0x2f8509[_0x428bac(0x1ad)]}),_0x289e07=_0x212d18[_0x155f0d(0x200)]({'seconds':_0x2f8509[_0x428bac(0x134)]});return{..._0x2f8509,'last_update_date':_0x212d18[_0x155f0d(0x236)]()[_0x155f0d(0x1de)](),'expires_date':_0x2ff92f[_0x155f0d(0x236)]()[_0x155f0d(0x1de)](),'expires_at':_0x2ff92f[_0x428bac(0x143)](),'refresh_token_expires_at':_0x289e07[_0x428bac(0x143)]()};}}exports[a5_0x4ee2c5(0x21f)]=HttpClient;
|