Initial commit with translated description
This commit is contained in:
140
lib/auth.js
Normal file
140
lib/auth.js
Normal file
@@ -0,0 +1,140 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Robust .env loading
|
||||
const possibleEnvPaths = [
|
||||
path.resolve(process.cwd(), '.env'),
|
||||
path.resolve(__dirname, '../../../.env'),
|
||||
path.resolve(__dirname, '../../../../.env')
|
||||
];
|
||||
|
||||
let envLoaded = false;
|
||||
for (const envPath of possibleEnvPaths) {
|
||||
if (fs.existsSync(envPath)) {
|
||||
try {
|
||||
require('dotenv').config({ path: envPath });
|
||||
envLoaded = true;
|
||||
break;
|
||||
} catch (e) {
|
||||
// Ignore load error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tokenCache = {
|
||||
token: null,
|
||||
expireTime: 0
|
||||
};
|
||||
|
||||
function loadConfig() {
|
||||
const configPath = path.join(__dirname, '../config.json');
|
||||
let config = {};
|
||||
if (fs.existsSync(configPath)) {
|
||||
try {
|
||||
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
} catch (e) {
|
||||
console.error("Failed to parse config.json");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
app_id: process.env.FEISHU_APP_ID || config.app_id,
|
||||
app_secret: process.env.FEISHU_APP_SECRET || config.app_secret
|
||||
};
|
||||
}
|
||||
|
||||
// Unified Token Cache (Shared with feishu-card and feishu-sticker)
|
||||
const TOKEN_CACHE_FILE = path.resolve(__dirname, '../../../memory/feishu_token.json');
|
||||
|
||||
async function getTenantAccessToken(forceRefresh = false) {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
// Try to load from disk first
|
||||
if (!forceRefresh && !tokenCache.token && fs.existsSync(TOKEN_CACHE_FILE)) {
|
||||
try {
|
||||
const saved = JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, 'utf8'));
|
||||
// Handle both 'expire' (standard) and 'expireTime' (legacy)
|
||||
const expiry = saved.expire || saved.expireTime;
|
||||
if (saved.token && expiry > now) {
|
||||
tokenCache.token = saved.token;
|
||||
tokenCache.expireTime = expiry; // Keep internal consistency
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore corrupted cache
|
||||
}
|
||||
}
|
||||
|
||||
// Force Refresh: Delete memory cache and file cache
|
||||
if (forceRefresh) {
|
||||
tokenCache.token = null;
|
||||
tokenCache.expireTime = 0;
|
||||
try { if (fs.existsSync(TOKEN_CACHE_FILE)) fs.unlinkSync(TOKEN_CACHE_FILE); } catch(e) {}
|
||||
}
|
||||
|
||||
if (tokenCache.token && tokenCache.expireTime > now) {
|
||||
return tokenCache.token;
|
||||
}
|
||||
|
||||
const config = loadConfig();
|
||||
if (!config.app_id || !config.app_secret) {
|
||||
throw new Error("Missing app_id or app_secret. Please set FEISHU_APP_ID and FEISHU_APP_SECRET environment variables or create a config.json file.");
|
||||
}
|
||||
|
||||
let lastError;
|
||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||
try {
|
||||
const response = await fetch('https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
"app_id": config.app_id,
|
||||
"app_secret": config.app_secret
|
||||
}),
|
||||
timeout: 5000 // 5s timeout
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code !== 0) {
|
||||
throw new Error(`Failed to get tenant_access_token: ${data.msg}`);
|
||||
}
|
||||
|
||||
tokenCache.token = data.tenant_access_token;
|
||||
tokenCache.expireTime = now + data.expire - 60; // Refresh 1 minute early
|
||||
|
||||
// Persist to disk (Unified Format)
|
||||
try {
|
||||
const cacheDir = path.dirname(TOKEN_CACHE_FILE);
|
||||
if (!fs.existsSync(cacheDir)) {
|
||||
fs.mkdirSync(cacheDir, { recursive: true });
|
||||
}
|
||||
// Save using 'expire' to match other skills
|
||||
fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify({
|
||||
token: tokenCache.token,
|
||||
expire: tokenCache.expireTime
|
||||
}, null, 2));
|
||||
} catch (e) {
|
||||
console.error("Failed to save token cache:", e.message);
|
||||
}
|
||||
|
||||
return tokenCache.token;
|
||||
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
if (attempt < 3) {
|
||||
const delay = 1000 * Math.pow(2, attempt - 1);
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError || new Error("Failed to retrieve access token after retries");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getTenantAccessToken
|
||||
};
|
||||
Reference in New Issue
Block a user