@lark-project/cli
Version:
飞书项目插件开发工具
175 lines (174 loc) • 6.74 kB
JavaScript
"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;