UNPKG

@lark-project/cli

Version:

飞书项目插件开发工具

175 lines (174 loc) 6.74 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.authSource = exports.hasDeveloperToken = exports.resolveToken = exports.loadEffectiveProfile = exports.touchLastUsed = exports.saveProfile = exports.loadProfile = exports.saveStore = exports.loadStore = exports.resolveConfigPath = exports.resolveConfigDir = void 0; const fs_1 = __importDefault(require("fs")); const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); const ENV_CONFIG_DIR = 'LPM_CONFIG_DIR'; const ENV_SERVER_URL = 'LPM_SERVER_URL'; const ENV_ACCESS_TOKEN = 'LPM_ACCESS_TOKEN'; const ENV_REFRESH_TOKEN = 'LPM_REFRESH_TOKEN'; const CONFIG_FILE_NAME = 'auth.json'; function resolveConfigDir() { const envDir = (process.env[ENV_CONFIG_DIR] || '').trim(); if (envDir) return envDir; return path_1.default.join(os_1.default.homedir(), '.lpm'); } exports.resolveConfigDir = resolveConfigDir; function resolveConfigPath() { return path_1.default.join(resolveConfigDir(), CONFIG_FILE_NAME); } exports.resolveConfigPath = resolveConfigPath; /** Normalize a URL to its origin (e.g. https://meego.example.com) */ function normalizeOrigin(url) { return new URL(url.replace(/\/+$/, '')).origin; } /** * Detect and migrate legacy single-domain auth.json to multi-domain format. * Returns null if file is already multi-domain or does not exist. */ function migrateLegacy(data) { if (data && typeof data === 'object' && typeof data.serverUrl === 'string' && typeof data.accessToken === 'string') { const legacy = data; const origin = normalizeOrigin(legacy.serverUrl); const profile = { accessToken: legacy.accessToken, accessTokenExpiresAt: legacy.accessTokenExpiresAt, refreshToken: legacy.refreshToken, refreshTokenExpiresAt: legacy.refreshTokenExpiresAt, clientId: legacy.clientId, tokenId: legacy.tokenId, }; const store = { [origin]: profile }; // Persist migrated format const configPath = resolveConfigPath(); fs_1.default.writeFileSync(configPath, JSON.stringify(store, null, 2), { mode: 0o644 }); return store; } return null; } /** Load the full multi-domain auth store from disk. */ function loadStore() { try { const raw = fs_1.default.readFileSync(resolveConfigPath(), 'utf-8'); const data = JSON.parse(raw); // Check for legacy format and migrate const migrated = migrateLegacy(data); if (migrated) return migrated; return data || {}; } catch (_a) { return {}; } } exports.loadStore = loadStore; /** Save the full multi-domain auth store to disk. */ function saveStore(store) { const configPath = resolveConfigPath(); fs_1.default.mkdirSync(path_1.default.dirname(configPath), { recursive: true }); fs_1.default.writeFileSync(configPath, JSON.stringify(store, null, 2), { mode: 0o644 }); } exports.saveStore = saveStore; /** Load the auth profile for a specific domain. */ function loadProfile(serverUrl) { const store = loadStore(); const origin = normalizeOrigin(serverUrl); const profile = store[origin] || null; if (!profile) return null; // Apply env var overrides const envToken = (process.env[ENV_ACCESS_TOKEN] || '').trim(); const envRefresh = (process.env[ENV_REFRESH_TOKEN] || '').trim(); if (envToken) profile.accessToken = envToken; if (envRefresh) profile.refreshToken = envRefresh; return profile; } exports.loadProfile = loadProfile; /** Save/update the auth profile for a specific domain (merge, not overwrite). */ function saveProfile(serverUrl, update) { const store = loadStore(); const origin = normalizeOrigin(serverUrl); store[origin] = Object.assign(Object.assign({}, store[origin]), update); saveStore(store); } exports.saveProfile = saveProfile; /** * 给某域名打上「最近使用」时间戳(unix 秒)。仅当该 origin 已是文件里的真实登录态 * 时才写——纯环境变量来源 / 未登录的 origin 不落盘,避免污染 auth.json。 * * 写失败静默吞掉:这只是给 whoami 用的「最近使用」提示,绝不能因为落盘失败 * (磁盘满 / 权限)把一次本该成功的鉴权拖垮(与 loadStore 读失败返回 {} 同策略)。 */ function touchLastUsed(serverUrl) { try { const origin = normalizeOrigin(serverUrl); const store = loadStore(); if (!store[origin]) return; store[origin] = Object.assign(Object.assign({}, store[origin]), { lastUsedAt: Math.floor(Date.now() / 1000) }); saveStore(store); } catch (_a) { /* 最近使用时间戳是尽力而为,写不进去不影响鉴权 */ } } exports.touchLastUsed = touchLastUsed; /** * Load effective auth profile for a domain. * Falls back to env vars if no file-based profile exists. */ function loadEffectiveProfile(serverUrl) { const profile = loadProfile(serverUrl); if (profile) return profile; // Fallback: env vars only const envUrl = (process.env[ENV_SERVER_URL] || '').trim(); const envToken = (process.env[ENV_ACCESS_TOKEN] || '').trim(); if (!envToken) return null; // Only return env-based profile if domain matches (or no domain constraint) if (envUrl) { try { const envOrigin = normalizeOrigin(envUrl); const targetOrigin = normalizeOrigin(serverUrl); if (envOrigin !== targetOrigin) return null; } catch (_a) { return null; } } return { accessToken: envToken, refreshToken: (process.env[ENV_REFRESH_TOKEN] || '').trim() || undefined }; } exports.loadEffectiveProfile = loadEffectiveProfile; /** * Resolve the effective Bearer token for a domain. * Priority: developerToken > accessToken * Returns the token string, or null if none available. */ function resolveToken(profile) { if (profile.developerToken) return profile.developerToken; if (profile.accessToken) return profile.accessToken; return null; } exports.resolveToken = resolveToken; /** * Check whether the profile has a permanent developer token. */ function hasDeveloperToken(profile) { return Boolean(profile.developerToken); } exports.hasDeveloperToken = hasDeveloperToken; function authSource() { return (process.env[ENV_ACCESS_TOKEN] || '').trim() ? 'env' : 'config'; } exports.authSource = authSource;