py-auth-client 0.1.4 → 0.1.5
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 +5 -21
- package/dist/cache.d.ts +14 -8
- package/dist/cache.js +130 -152
- package/dist/client.d.ts +16 -4
- package/dist/client.js +296 -98
- package/dist/device.d.ts +3 -3
- package/dist/device.js +390 -89
- package/dist/devicePlatform.d.ts +11 -0
- package/dist/devicePlatform.js +87 -0
- package/dist/devicePlatformFallback.d.ts +10 -0
- package/dist/devicePlatformFallback.js +27 -0
- package/dist/devicePlatformPosix.d.ts +9 -0
- package/dist/devicePlatformPosix.js +277 -0
- package/dist/devicePlatformShared.d.ts +21 -0
- package/dist/devicePlatformShared.js +110 -0
- package/dist/devicePlatformWindows.d.ts +11 -0
- package/dist/devicePlatformWindows.js +218 -0
- package/dist/errors.d.ts +0 -3
- package/dist/errors.js +0 -17
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/dist/sdkMeta.d.ts +5 -0
- package/dist/sdkMeta.js +15 -0
- package/dist/stateBundle.d.ts +11 -0
- package/dist/stateBundle.js +211 -0
- package/dist/storage.d.ts +1 -0
- package/dist/storage.js +20 -0
- package/dist/types.d.ts +61 -19
- package/package.json +7 -7
package/dist/client.js
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.AuthClient = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
4
9
|
const errors_1 = require("./errors");
|
|
5
10
|
const crypto_1 = require("./crypto");
|
|
6
11
|
const cache_1 = require("./cache");
|
|
7
12
|
const device_1 = require("./device");
|
|
8
13
|
const http_1 = require("./http");
|
|
14
|
+
const storage_1 = require("./storage");
|
|
15
|
+
const stateBundle_1 = require("./stateBundle");
|
|
16
|
+
const sdkMeta_1 = require("./sdkMeta");
|
|
17
|
+
const SECONDS_PER_MINUTE = 60;
|
|
18
|
+
const SECONDS_PER_HOUR = 3600;
|
|
19
|
+
const SECONDS_PER_DAY = 86400;
|
|
20
|
+
const HEARTBEAT_TIMEOUT_MS = 2_000;
|
|
21
|
+
const HEARTBEAT_LIGHT_TIMEOUT_MS = 4_000;
|
|
9
22
|
class AuthClient {
|
|
10
23
|
serverUrl;
|
|
11
24
|
softwareName;
|
|
12
25
|
softwareVersion;
|
|
13
26
|
deviceId;
|
|
14
27
|
deviceInfo;
|
|
28
|
+
deviceInfoDeferred;
|
|
15
29
|
clientSecret;
|
|
16
30
|
debug;
|
|
17
31
|
cache;
|
|
32
|
+
stateBundleExistedBeforeInit;
|
|
18
33
|
constructor(config) {
|
|
19
34
|
if (!config.serverUrl)
|
|
20
35
|
throw new Error("serverUrl不能为空");
|
|
@@ -29,43 +44,82 @@ class AuthClient {
|
|
|
29
44
|
this.softwareVersion = config.softwareVersion ?? "0.0.0";
|
|
30
45
|
this.clientSecret = secret;
|
|
31
46
|
this.debug = !!config.debug;
|
|
32
|
-
this.deviceId = (0, device_1.buildDeviceId)(this.serverUrl, config.deviceId, this.softwareName);
|
|
33
|
-
this.deviceInfo = {
|
|
34
|
-
...(config.deviceInfo ?? (0, device_1.collectDeviceInfo)()),
|
|
35
|
-
software_version: this.softwareVersion,
|
|
36
|
-
};
|
|
37
|
-
const enableCache = config.enableCache ?? true;
|
|
38
47
|
const cacheValidityDays = config.cacheValidityDays ?? 7;
|
|
39
48
|
const checkIntervalDays = config.checkIntervalDays ?? 2;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
const storageBase = (0, storage_1.getClientStorageRoot)();
|
|
50
|
+
const stateBundleExistedBeforeInit = node_fs_1.default.existsSync((0, stateBundle_1.bundlePath)(this.serverUrl, storageBase));
|
|
51
|
+
const hasStableDeviceId = !!config.deviceId || !!(0, device_1.loadPersistedDeviceId)(this.serverUrl, this.softwareName, storageBase);
|
|
52
|
+
this.deviceId = (0, device_1.buildDeviceId)(this.serverUrl, config.deviceId, this.softwareName, storageBase);
|
|
53
|
+
if (config.deviceInfo) {
|
|
54
|
+
this.deviceInfoDeferred = false;
|
|
55
|
+
this.deviceInfo = {
|
|
56
|
+
...config.deviceInfo,
|
|
57
|
+
software_version: this.softwareVersion,
|
|
58
|
+
sdk: (0, sdkMeta_1.baseSdk)(process.version),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
else if (hasStableDeviceId) {
|
|
62
|
+
this.deviceInfoDeferred = true;
|
|
63
|
+
this.deviceInfo = {
|
|
64
|
+
software_version: this.softwareVersion,
|
|
65
|
+
sdk: (0, sdkMeta_1.baseSdk)(process.version),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.deviceInfoDeferred = false;
|
|
70
|
+
this.deviceInfo = {
|
|
71
|
+
...(0, device_1.collectDeviceInfo)(),
|
|
72
|
+
software_version: this.softwareVersion,
|
|
73
|
+
sdk: (0, sdkMeta_1.baseSdk)(process.version),
|
|
74
|
+
};
|
|
49
75
|
}
|
|
76
|
+
this.cache = new cache_1.AuthCache({
|
|
77
|
+
storageRoot: storageBase,
|
|
78
|
+
deviceId: this.deviceId,
|
|
79
|
+
serverUrl: this.serverUrl,
|
|
80
|
+
softwareName: this.softwareName,
|
|
81
|
+
cacheValidityDays,
|
|
82
|
+
checkIntervalDays,
|
|
83
|
+
});
|
|
84
|
+
if (this.deviceInfoDeferred) {
|
|
85
|
+
const snap = this.cache.loadDeviceInfoSnapshot();
|
|
86
|
+
if (snap && typeof snap === "object") {
|
|
87
|
+
this.deviceInfo = {
|
|
88
|
+
...snap,
|
|
89
|
+
software_version: this.softwareVersion,
|
|
90
|
+
sdk: (0, sdkMeta_1.baseSdk)(process.version),
|
|
91
|
+
};
|
|
92
|
+
this.deviceInfoDeferred = false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
this.stateBundleExistedBeforeInit = stateBundleExistedBeforeInit;
|
|
50
96
|
}
|
|
51
97
|
logDebug(msg) {
|
|
52
98
|
if (this.debug) {
|
|
53
|
-
|
|
54
|
-
// eslint-disable-next-line no-console
|
|
55
|
-
console.debug(`[ts-auth-client][DEBUG] ${msg}`);
|
|
99
|
+
console.debug(`[ts][DEBUG] ${msg}`);
|
|
56
100
|
}
|
|
57
101
|
}
|
|
102
|
+
ensureFullDeviceInfo() {
|
|
103
|
+
if (!this.deviceInfoDeferred)
|
|
104
|
+
return;
|
|
105
|
+
this.deviceInfo = {
|
|
106
|
+
...(0, device_1.collectDeviceInfo)(),
|
|
107
|
+
software_version: this.softwareVersion,
|
|
108
|
+
sdk: (0, sdkMeta_1.baseSdk)(process.version),
|
|
109
|
+
};
|
|
110
|
+
this.deviceInfoDeferred = false;
|
|
111
|
+
}
|
|
58
112
|
formatRemainingTime(cachedAtSeconds) {
|
|
59
|
-
if (!cachedAtSeconds || cachedAtSeconds <= 0
|
|
113
|
+
if (!cachedAtSeconds || cachedAtSeconds <= 0)
|
|
60
114
|
return "未知";
|
|
61
115
|
const now = Date.now() / 1000;
|
|
62
116
|
const elapsed = now - cachedAtSeconds;
|
|
63
117
|
const remaining = this.cache.cacheValiditySeconds - elapsed;
|
|
64
118
|
if (remaining <= 0)
|
|
65
119
|
return "已过期";
|
|
66
|
-
const days = Math.floor(remaining /
|
|
67
|
-
const hours = Math.floor((remaining %
|
|
68
|
-
const minutes = Math.floor((remaining %
|
|
120
|
+
const days = Math.floor(remaining / SECONDS_PER_DAY);
|
|
121
|
+
const hours = Math.floor((remaining % SECONDS_PER_DAY) / SECONDS_PER_HOUR);
|
|
122
|
+
const minutes = Math.floor((remaining % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
|
|
69
123
|
const parts = [];
|
|
70
124
|
if (days > 0)
|
|
71
125
|
parts.push(`${days}天`);
|
|
@@ -75,37 +129,55 @@ class AuthClient {
|
|
|
75
129
|
parts.push(`${minutes}分钟`);
|
|
76
130
|
return parts.join("");
|
|
77
131
|
}
|
|
78
|
-
async
|
|
132
|
+
async postHeartbeatDeviceInfo(deviceInfoPayload, heartbeatTimes, timeoutMs) {
|
|
133
|
+
const device_info = {
|
|
134
|
+
...deviceInfoPayload,
|
|
135
|
+
sdk: {
|
|
136
|
+
...(deviceInfoPayload.sdk ?? (0, sdkMeta_1.baseSdk)(process.version)),
|
|
137
|
+
heartbeat_times: heartbeatTimes,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
const requestData = {
|
|
141
|
+
device_id: this.deviceId,
|
|
142
|
+
software_name: this.softwareName,
|
|
143
|
+
device_info,
|
|
144
|
+
};
|
|
145
|
+
const encrypted = (0, crypto_1.encryptData)(JSON.stringify(requestData), this.clientSecret);
|
|
146
|
+
const { status, json, text } = await (0, http_1.postJson)(`${this.serverUrl}/api/auth/heartbeat`, { encrypted_data: encrypted }, timeoutMs);
|
|
147
|
+
if (status === 200) {
|
|
148
|
+
const token = json?.encrypted_data ?? "";
|
|
149
|
+
const decryptedText = token ? (0, crypto_1.decryptData)(token, this.clientSecret) : null;
|
|
150
|
+
if (!decryptedText) {
|
|
151
|
+
this.logDebug("在线订阅响应解密失败");
|
|
152
|
+
return { authorized: false, message: "解密响应失败", success: false, from_cache: false };
|
|
153
|
+
}
|
|
154
|
+
const decrypted = JSON.parse(decryptedText);
|
|
155
|
+
this.logDebug(`在线订阅成功,authorized=${!!decrypted.authorized}`);
|
|
156
|
+
return {
|
|
157
|
+
authorized: !!decrypted.authorized,
|
|
158
|
+
message: decrypted.message ?? "",
|
|
159
|
+
success: true,
|
|
160
|
+
from_cache: false,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const detail = json?.detail;
|
|
164
|
+
const msg = status === 403 && typeof detail === "string" ? detail : `服务器错误: ${status}`;
|
|
165
|
+
this.logDebug(`在线订阅失败,status=${status}, message=${msg}; raw=${text}`);
|
|
166
|
+
return { authorized: false, message: msg, success: false, from_cache: false, is_auth_error: status === 403 };
|
|
167
|
+
}
|
|
168
|
+
async checkOnlineLight(heartbeatTimes) {
|
|
79
169
|
try {
|
|
80
|
-
this.logDebug("
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
170
|
+
this.logDebug("轻量在线订阅请求(不等待全量 device_info / 公网 IP)...");
|
|
171
|
+
const light = {
|
|
172
|
+
...this.deviceInfo,
|
|
173
|
+
software_version: this.softwareVersion,
|
|
174
|
+
system: {
|
|
175
|
+
...(this.deviceInfo.system ?? {}),
|
|
176
|
+
hostname: this.deviceInfo.system?.hostname?.trim() || node_os_1.default.hostname(),
|
|
177
|
+
os: this.deviceInfo.system?.os || process.platform,
|
|
178
|
+
},
|
|
85
179
|
};
|
|
86
|
-
|
|
87
|
-
const { status, json, text } = await (0, http_1.postJson)(`${this.serverUrl}/api/auth/heartbeat`, { encrypted_data: encrypted }, 10_000);
|
|
88
|
-
if (status === 200) {
|
|
89
|
-
const token = json?.encrypted_data ?? "";
|
|
90
|
-
const decryptedText = token ? (0, crypto_1.decryptData)(token, this.clientSecret) : null;
|
|
91
|
-
if (!decryptedText) {
|
|
92
|
-
this.logDebug("在线订阅响应解密失败");
|
|
93
|
-
return { authorized: false, message: "解密响应失败", success: false, from_cache: false };
|
|
94
|
-
}
|
|
95
|
-
const decrypted = JSON.parse(decryptedText);
|
|
96
|
-
this.logDebug(`在线订阅成功,authorized=${!!decrypted.authorized}`);
|
|
97
|
-
return {
|
|
98
|
-
authorized: !!decrypted.authorized,
|
|
99
|
-
message: decrypted.message ?? "",
|
|
100
|
-
success: true,
|
|
101
|
-
from_cache: false,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
// Python 逻辑:403 取 detail,其它返回 “服务器错误: status”
|
|
105
|
-
const detail = json?.detail;
|
|
106
|
-
const msg = status === 403 && typeof detail === "string" ? detail : `服务器错误: ${status}`;
|
|
107
|
-
this.logDebug(`在线订阅失败,status=${status}, message=${msg}; raw=${text}`);
|
|
108
|
-
return { authorized: false, message: msg, success: false, from_cache: false, is_auth_error: status === 403 };
|
|
180
|
+
return await this.postHeartbeatDeviceInfo(light, heartbeatTimes, HEARTBEAT_LIGHT_TIMEOUT_MS);
|
|
109
181
|
}
|
|
110
182
|
catch (e) {
|
|
111
183
|
const msg = e?.name === "AbortError" ? "连接失败: timeout" : `连接失败: ${String(e?.message ?? e)}`;
|
|
@@ -113,29 +185,68 @@ class AuthClient {
|
|
|
113
185
|
return { authorized: false, message: msg, success: false, from_cache: false };
|
|
114
186
|
}
|
|
115
187
|
}
|
|
116
|
-
async
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
188
|
+
async checkOnline(heartbeatTimes) {
|
|
189
|
+
try {
|
|
190
|
+
const hasPublicIp = typeof this.deviceInfo.network?.public_ip === "string" &&
|
|
191
|
+
this.deviceInfo.network.public_ip.trim() !== "";
|
|
192
|
+
const needPublicIp = this.deviceInfoDeferred || !hasPublicIp;
|
|
193
|
+
const pubPromise = needPublicIp ? (0, device_1.fetchPublicIp)() : Promise.resolve("");
|
|
194
|
+
this.ensureFullDeviceInfo();
|
|
195
|
+
this.logDebug("开始在线订阅请求...");
|
|
196
|
+
const pub = await pubPromise;
|
|
197
|
+
const nw = { ...(this.deviceInfo.network ?? {}) };
|
|
198
|
+
if (pub)
|
|
199
|
+
nw.public_ip = pub;
|
|
200
|
+
if (pub) {
|
|
201
|
+
this.deviceInfo = { ...this.deviceInfo, network: nw };
|
|
127
202
|
}
|
|
203
|
+
const device_info = {
|
|
204
|
+
...this.deviceInfo,
|
|
205
|
+
...(Object.keys(nw).length > 0 ? { network: nw } : {}),
|
|
206
|
+
};
|
|
207
|
+
return await this.postHeartbeatDeviceInfo(device_info, heartbeatTimes, HEARTBEAT_TIMEOUT_MS);
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
const msg = e?.name === "AbortError" ? "连接失败: timeout" : `连接失败: ${String(e?.message ?? e)}`;
|
|
211
|
+
this.logDebug(`在线订阅请求异常: ${msg}`);
|
|
212
|
+
return { authorized: false, message: msg, success: false, from_cache: false };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
persistOnlineResult(result, heartbeatIfAuthorized) {
|
|
216
|
+
const snap = result.authorized
|
|
217
|
+
? JSON.parse(JSON.stringify(this.deviceInfo))
|
|
218
|
+
: undefined;
|
|
219
|
+
this.cache.saveCache(result.authorized, result.message, result.authorized ? heartbeatIfAuthorized : undefined, snap);
|
|
220
|
+
}
|
|
221
|
+
async checkAuthorization(_forceOnline = false) {
|
|
222
|
+
if (this.debug) {
|
|
223
|
+
const cf = this.cache.cacheFile;
|
|
224
|
+
const fileNow = node_fs_1.default.existsSync(cf);
|
|
225
|
+
const pre = this.stateBundleExistedBeforeInit;
|
|
226
|
+
let desc = "不存在(持久化可能失败)";
|
|
227
|
+
if (fileNow && pre)
|
|
228
|
+
desc = "启动前已存在";
|
|
229
|
+
else if (fileNow && !pre)
|
|
230
|
+
desc = "启动前不存在,构造客户端时已新建(device_id 持久化)";
|
|
231
|
+
else if (!fileNow && pre)
|
|
232
|
+
desc = "启动前曾有,当前缺失(异常)";
|
|
233
|
+
this.logDebug(`状态包: ${cf} | ${desc}`);
|
|
128
234
|
}
|
|
235
|
+
const snap = this.cache.snapshotForAuthorizationCheck();
|
|
236
|
+
const cacheData = snap.cacheData;
|
|
237
|
+
const cacheValid = this.cache.isCacheTTLValid(cacheData);
|
|
129
238
|
if (cacheValid) {
|
|
239
|
+
this.logDebug("本地缓存仍在有效期内(在线失败时可作后备)");
|
|
130
240
|
this.logDebug("缓存有效,继续尝试在线订阅来更新订阅");
|
|
131
241
|
}
|
|
132
242
|
else {
|
|
133
243
|
this.logDebug(cacheData ? "缓存存在但已过期,准备发起在线订阅请求" : "未找到缓存,准备发起在线订阅请求");
|
|
134
244
|
}
|
|
135
|
-
const
|
|
245
|
+
const nextHb = snap.storedHeartbeatTimes + 1;
|
|
246
|
+
const onlineResult = await this.checkOnline(nextHb);
|
|
136
247
|
if (onlineResult.success) {
|
|
137
248
|
this.logDebug("在线订阅成功,更新缓存");
|
|
138
|
-
this.
|
|
249
|
+
this.persistOnlineResult(onlineResult, nextHb);
|
|
139
250
|
return onlineResult;
|
|
140
251
|
}
|
|
141
252
|
if (cacheValid && cacheData) {
|
|
@@ -150,17 +261,80 @@ class AuthClient {
|
|
|
150
261
|
}
|
|
151
262
|
return onlineResult;
|
|
152
263
|
}
|
|
153
|
-
async
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
264
|
+
async checkAuthorizationProgressive(_forceOnline = false) {
|
|
265
|
+
if (this.debug) {
|
|
266
|
+
const cf = this.cache.cacheFile;
|
|
267
|
+
const fileNow = node_fs_1.default.existsSync(cf);
|
|
268
|
+
const pre = this.stateBundleExistedBeforeInit;
|
|
269
|
+
let desc = "不存在(持久化可能失败)";
|
|
270
|
+
if (fileNow && pre)
|
|
271
|
+
desc = "启动前已存在";
|
|
272
|
+
else if (fileNow && !pre)
|
|
273
|
+
desc = "启动前不存在,构造客户端时已新建(device_id 持久化)";
|
|
274
|
+
else if (!fileNow && pre)
|
|
275
|
+
desc = "启动前曾有,当前缺失(异常)";
|
|
276
|
+
this.logDebug(`状态包: ${cf} | ${desc}`);
|
|
277
|
+
}
|
|
278
|
+
const snap = this.cache.snapshotForAuthorizationCheck();
|
|
279
|
+
const cacheData = snap.cacheData;
|
|
280
|
+
const cacheValid = this.cache.isCacheTTLValid(cacheData);
|
|
281
|
+
if (cacheValid) {
|
|
282
|
+
this.logDebug("本地缓存仍在有效期内(在线失败时可作后备)");
|
|
283
|
+
this.logDebug("缓存有效,继续尝试在线订阅来更新订阅");
|
|
162
284
|
}
|
|
163
|
-
|
|
285
|
+
else {
|
|
286
|
+
this.logDebug(cacheData ? "缓存存在但已过期,准备发起在线订阅请求" : "未找到缓存,准备发起在线订阅请求");
|
|
287
|
+
}
|
|
288
|
+
const nextHb = snap.storedHeartbeatTimes + 1;
|
|
289
|
+
const rFast = await this.checkOnlineLight(nextHb);
|
|
290
|
+
if (rFast.success) {
|
|
291
|
+
if (rFast.authorized) {
|
|
292
|
+
this.logDebug("在线订阅成功,更新缓存");
|
|
293
|
+
this.persistOnlineResult(rFast, nextHb);
|
|
294
|
+
this.logDebug("轻量心跳已落盘,发起全量 device_info 补全心跳...");
|
|
295
|
+
const rFull = await this.checkOnline(nextHb + 1);
|
|
296
|
+
if (rFull.success) {
|
|
297
|
+
this.logDebug("在线订阅成功,更新缓存");
|
|
298
|
+
this.persistOnlineResult(rFull, rFull.authorized ? nextHb + 1 : undefined);
|
|
299
|
+
return rFull;
|
|
300
|
+
}
|
|
301
|
+
const gc = this.cache.getCache();
|
|
302
|
+
if (gc && this.cache.isCacheTTLValid(gc)) {
|
|
303
|
+
const remaining = this.formatRemainingTime(gc.cachedAt);
|
|
304
|
+
this.logDebug(`补全心跳失败,沿用轻量结果,订阅剩余时间: ${remaining}`);
|
|
305
|
+
return {
|
|
306
|
+
authorized: gc.authorized,
|
|
307
|
+
message: gc.message,
|
|
308
|
+
success: true,
|
|
309
|
+
from_cache: true,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return rFull;
|
|
313
|
+
}
|
|
314
|
+
this.persistOnlineResult(rFast, undefined);
|
|
315
|
+
return rFast;
|
|
316
|
+
}
|
|
317
|
+
const onlineResult = await this.checkOnline(nextHb);
|
|
318
|
+
if (onlineResult.success) {
|
|
319
|
+
this.logDebug("在线订阅成功,更新缓存");
|
|
320
|
+
this.persistOnlineResult(onlineResult, onlineResult.authorized ? nextHb : undefined);
|
|
321
|
+
return onlineResult;
|
|
322
|
+
}
|
|
323
|
+
if (cacheValid && cacheData) {
|
|
324
|
+
const remaining = this.formatRemainingTime(cacheData.cachedAt);
|
|
325
|
+
this.logDebug(`在线订阅失败,但缓存有效,使用缓存结果,订阅剩余时间: ${remaining}`);
|
|
326
|
+
return {
|
|
327
|
+
authorized: cacheData.authorized,
|
|
328
|
+
message: cacheData.message,
|
|
329
|
+
success: true,
|
|
330
|
+
from_cache: true,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
return onlineResult;
|
|
334
|
+
}
|
|
335
|
+
async requireAuthorization(forceOnline = false) {
|
|
336
|
+
const result = await this.checkAuthorization(forceOnline);
|
|
337
|
+
if (!result.success || !result.authorized) {
|
|
164
338
|
throw new errors_1.AuthorizationError({
|
|
165
339
|
message: result.message,
|
|
166
340
|
result,
|
|
@@ -171,35 +345,59 @@ class AuthClient {
|
|
|
171
345
|
return true;
|
|
172
346
|
}
|
|
173
347
|
clearCache() {
|
|
174
|
-
if (!this.cache)
|
|
175
|
-
return true;
|
|
176
348
|
return this.cache.clearCache();
|
|
177
349
|
}
|
|
350
|
+
canSoftLaunch() {
|
|
351
|
+
const c = this.cache.getCache();
|
|
352
|
+
return !!(c?.authorized && this.cache.isCacheTTLValid(c));
|
|
353
|
+
}
|
|
354
|
+
startBackgroundRefresh(options) {
|
|
355
|
+
const soft = this.canSoftLaunch();
|
|
356
|
+
const fo = options?.forceOnline ?? false;
|
|
357
|
+
void this.checkAuthorizationProgressive(fo).then((r) => {
|
|
358
|
+
options?.onDone?.(r);
|
|
359
|
+
}, (err) => {
|
|
360
|
+
options?.onDone?.({
|
|
361
|
+
authorized: false,
|
|
362
|
+
success: false,
|
|
363
|
+
from_cache: false,
|
|
364
|
+
message: err instanceof Error ? err.message : String(err),
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
return soft;
|
|
368
|
+
}
|
|
178
369
|
async getAuthorizationInfo() {
|
|
179
|
-
const
|
|
180
|
-
const info =
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
info.cached_at = cache.cachedAt;
|
|
195
|
-
if (cache.cachedAt > 0) {
|
|
196
|
-
info.cached_at_readable = new Date(cache.cachedAt * 1000).toISOString().replace("T", " ").slice(0, 19);
|
|
197
|
-
}
|
|
370
|
+
const cache = this.cache.getCache();
|
|
371
|
+
const info = cache
|
|
372
|
+
? {
|
|
373
|
+
authorized: cache.authorized,
|
|
374
|
+
success: true,
|
|
375
|
+
from_cache: true,
|
|
376
|
+
message: cache.message,
|
|
377
|
+
device_id: this.deviceId,
|
|
378
|
+
server_url: this.serverUrl,
|
|
379
|
+
remaining_time: this.formatRemainingTime(cache.cachedAt),
|
|
380
|
+
cache_valid: this.cache.isCacheTTLValid(cache),
|
|
381
|
+
cached_at: cache.cachedAt,
|
|
382
|
+
cached_at_readable: cache.cachedAt > 0
|
|
383
|
+
? new Date(cache.cachedAt * 1000).toISOString().replace("T", " ").slice(0, 19)
|
|
384
|
+
: undefined,
|
|
198
385
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
386
|
+
: {
|
|
387
|
+
authorized: false,
|
|
388
|
+
success: false,
|
|
389
|
+
from_cache: false,
|
|
390
|
+
message: "无本地授权缓存",
|
|
391
|
+
device_id: this.deviceId,
|
|
392
|
+
server_url: this.serverUrl,
|
|
393
|
+
remaining_time: "无缓存",
|
|
394
|
+
cache_valid: false,
|
|
395
|
+
};
|
|
396
|
+
if (this.debug) {
|
|
397
|
+
try {
|
|
398
|
+
this.logDebug(`授权信息摘要:\n${JSON.stringify(info, null, 2)}`);
|
|
202
399
|
}
|
|
400
|
+
catch { }
|
|
203
401
|
}
|
|
204
402
|
return info;
|
|
205
403
|
}
|
package/dist/device.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DeviceInfo } from "./types";
|
|
2
|
-
export declare function loadPersistedDeviceId(serverUrl: string, softwareName: string): string | null;
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function buildDeviceId(serverUrl: string, providedDeviceId: string | undefined, softwareName: string): string;
|
|
2
|
+
export declare function loadPersistedDeviceId(serverUrl: string, softwareName: string, baseDir: string): string | null;
|
|
3
|
+
export declare function buildDeviceId(serverUrl: string, providedDeviceId: string | undefined, softwareName: string, baseDir: string): string;
|
|
5
4
|
export declare function collectDeviceInfo(): DeviceInfo;
|
|
5
|
+
export declare function fetchPublicIp(): Promise<string>;
|