UNPKG

@gooin/garmin-connect

Version:

Makes it simple to interface with Garmin Connect to get or set any data point

247 lines 9.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FileMFASessionStorage = void 0; const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); /** * 文件系统实现的MFA会话存储 */ class FileMFASessionStorage { constructor(storageDir) { this.DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5分钟 this.storageDir = storageDir; this.ensureStorageDir(); } /** * 确保存储目录存在 */ async ensureStorageDir() { try { await fs_1.promises.mkdir(this.storageDir, { recursive: true }); } catch (error) { console.error('创建MFA存储目录失败:', error); } } /** * 获取会话文件路径 */ getSessionFilePath(sessionId) { return path_1.default.join(this.storageDir, `${sessionId}.json`); } /** * 创建一个等待MFA验证码的Promise * @param sessionId 会话ID * @param timeout 超时时间(毫秒) * @returns Promise<string> 返回验证码 */ async waitForMFACode(sessionId, timeout = this.DEFAULT_TIMEOUT) { // 确保存储目录存在 await this.ensureStorageDir(); // 清理可能存在的旧会话 await this.cleanupRequest(sessionId); // 创建会话文件 const sessionData = { sessionId, status: 'waiting', createdAt: new Date().toISOString(), timeout: timeout }; const sessionFilePath = this.getSessionFilePath(sessionId); await fs_1.promises.writeFile(sessionFilePath, JSON.stringify(sessionData), 'utf8'); console.log(`MFA验证会话已创建: ${sessionId}`); // 轮询检查验证码是否已提交 return new Promise((resolve, reject) => { const startTime = Date.now(); const checkInterval = setInterval(async () => { try { // 检查是否超时 if (Date.now() - startTime > timeout) { clearInterval(checkInterval); await this.cleanupRequest(sessionId); reject(new Error('MFA验证超时,请重新登录')); return; } // 检查会话文件是否存在 try { const data = await fs_1.promises.readFile(sessionFilePath, 'utf8'); const session = JSON.parse(data); // 如果状态已更新为resolved,则返回验证码 if (session.status === 'resolved' && session.code) { clearInterval(checkInterval); await this.cleanupRequest(sessionId); resolve(session.code); return; } // 如果状态已更新为rejected,则抛出错误 if (session.status === 'rejected' && session.error) { clearInterval(checkInterval); await this.cleanupRequest(sessionId); reject(new Error(session.error)); return; } } catch (error) { // 文件不存在或其他错误,继续等待 } } catch (error) { clearInterval(checkInterval); await this.cleanupRequest(sessionId); reject(error instanceof Error ? error : new Error(String(error))); } }, 1000); // 每秒检查一次 }); } /** * 提交MFA验证码 * @param sessionId 会话ID * @param code 验证码 * @returns 是否成功提交 */ async submitMFACode(sessionId, code) { const sessionFilePath = this.getSessionFilePath(sessionId); try { // 检查会话文件是否存在 const data = await fs_1.promises.readFile(sessionFilePath, 'utf8'); const session = JSON.parse(data); if (session.status !== 'waiting') { console.log(`MFA验证会话状态不正确: ${sessionId}, 状态: ${session.status}`); return false; } // 更新会话状态为已解决 session.status = 'resolved'; session.code = code; session.resolvedAt = new Date().toISOString(); await fs_1.promises.writeFile(sessionFilePath, JSON.stringify(session), 'utf8'); console.log(`MFA验证码已提交: code ${code}, sessionId ${sessionId}`); return true; } catch (error) { console.log(`未找到MFA验证会话: ${sessionId}, 错误: ${error instanceof Error ? error.message : String(error)}`); return false; } } /** * 取消MFA验证 * @param sessionId 会话ID * @param reason 取消原因 * @returns 是否成功取消 */ async cancelMFARequest(sessionId, reason = 'MFA验证已取消') { const sessionFilePath = this.getSessionFilePath(sessionId); try { // 检查会话文件是否存在 const data = await fs_1.promises.readFile(sessionFilePath, 'utf8'); const session = JSON.parse(data); // 更新会话状态为已拒绝 session.status = 'rejected'; session.error = reason; session.rejectedAt = new Date().toISOString(); await fs_1.promises.writeFile(sessionFilePath, JSON.stringify(session), 'utf8'); console.log(`MFA验证已取消: ${sessionId}, 原因: ${reason}`); return true; } catch (error) { console.log(`未找到MFA验证会话: ${sessionId}, 错误: ${error instanceof Error ? error.message : String(error)}`); return false; } } /** * 检查会话是否存在 * @param sessionId 会话ID * @returns 是否存在 */ async hasSession(sessionId) { const sessionFilePath = this.getSessionFilePath(sessionId); try { await fs_1.promises.access(sessionFilePath); return true; } catch (_a) { return false; } } /** * 获取所有活跃的会话ID * @returns 会话ID列表 */ async getActiveSessions() { try { await this.ensureStorageDir(); const files = await fs_1.promises.readdir(this.storageDir); return files .filter((file) => file.endsWith('.json')) .map((file) => file.replace('.json', '')); } catch (error) { console.error('获取活跃会话失败:', error instanceof Error ? error : String(error)); return []; } } /** * 清理过期的请求 * @param maxAge 最大存活时间(毫秒) */ async cleanupExpiredRequests(maxAge = this.DEFAULT_TIMEOUT) { try { await this.ensureStorageDir(); const files = await fs_1.promises.readdir(this.storageDir); const now = new Date(); let cleanedCount = 0; for (const file of files) { if (!file.endsWith('.json')) continue; try { const filePath = path_1.default.join(this.storageDir, file); const data = await fs_1.promises.readFile(filePath, 'utf8'); const session = JSON.parse(data); const createdAt = new Date(session.createdAt); // 检查是否过期 if (now.getTime() - createdAt.getTime() > maxAge) { await fs_1.promises.unlink(filePath); cleanedCount++; } } catch (error) { // 如果文件读取失败,直接删除 try { await fs_1.promises.unlink(path_1.default.join(this.storageDir, file)); cleanedCount++; } catch (unlinkError) { console.error(`删除过期会话文件失败: ${file}`, unlinkError instanceof Error ? unlinkError : String(unlinkError)); } } } if (cleanedCount > 0) { console.log(`清理了 ${cleanedCount} 个过期的MFA验证会话`); } } catch (error) { console.error('清理过期请求失败:', error instanceof Error ? error : String(error)); } } /** * 清理指定会话的资源 * @param sessionId 会话ID */ async cleanupRequest(sessionId) { const sessionFilePath = this.getSessionFilePath(sessionId); try { await fs_1.promises.unlink(sessionFilePath); } catch (error) { // 文件不存在,忽略错误 } } } exports.FileMFASessionStorage = FileMFASessionStorage; //# sourceMappingURL=FileMFASessionStorage.js.map