Skip to main content
Glama
larksuite

Feishu/Lark OpenAPI MCP

Official
by larksuite
storage-manager.ts3.65 kB
import fs from 'fs'; import path from 'path'; import { EncryptionUtil } from './encryption'; import { AUTH_CONFIG } from '../config'; import { StorageData } from '../types'; import { logger } from '../../utils/logger'; export class StorageManager { private encryptionUtil: EncryptionUtil | undefined; private initializePromise: Promise<void> | undefined; private isInitializedStorageSuccess = false; constructor() { this.initialize(); } get storageFile(): string { return path.join(AUTH_CONFIG.STORAGE_DIR, AUTH_CONFIG.STORAGE_FILE); } private async initialize(): Promise<void> { if (this.initializePromise) { return this.initializePromise; } this.initializePromise = this.performInitialization(); await this.initializePromise; } private async performInitialization(): Promise<void> { try { await this.initializeEncryption(); this.ensureStorageDir(); this.isInitializedStorageSuccess = true; } catch (error) { logger.warn(`[StorageManager] Failed to initialize: ${error}`); logger.warn( '[StorageManager] ⚠️ Builtin User Access Token Store will be disabled. but you can still use it with memory store', ); this.isInitializedStorageSuccess = false; } } private async initializeEncryption(): Promise<void> { try { const keytar = await import('keytar'); let key = await keytar.getPassword(AUTH_CONFIG.SERVER_NAME, AUTH_CONFIG.AES_KEY_NAME); if (!key) { key = EncryptionUtil.generateKey(); await keytar.setPassword(AUTH_CONFIG.SERVER_NAME, AUTH_CONFIG.AES_KEY_NAME, key); } this.encryptionUtil = new EncryptionUtil(key); } catch (error) { logger.warn(`[StorageManager] Failed to initialize encryption: ${error}`); throw error; } } private ensureStorageDir(): void { if (!fs.existsSync(AUTH_CONFIG.STORAGE_DIR)) { fs.mkdirSync(AUTH_CONFIG.STORAGE_DIR, { recursive: true }); } } encrypt(data: string): string { if (!this.isInitializedStorageSuccess || !this.encryptionUtil) { throw new Error('StorageManager not initialized - call initialize() first'); } return this.encryptionUtil.encrypt(data); } decrypt(encryptedData: string): string { if (!this.isInitializedStorageSuccess || !this.encryptionUtil) { throw new Error('StorageManager not initialized - call initialize() first'); } return this.encryptionUtil.decrypt(encryptedData); } async loadStorageData(): Promise<StorageData> { await this.initialize(); if (!this.isInitializedStorageSuccess || !fs.existsSync(this.storageFile)) { return { tokens: {}, clients: {} }; } try { const data = fs.readFileSync(this.storageFile, 'utf8'); return data ? JSON.parse(this.decrypt(data)) : { tokens: {}, clients: {} }; } catch (error) { logger.error(`[StorageManager] Failed to load storage data: ${error}`); logger.error( '[StorageManager] ⚠️ Builtin User Access Token Store will be disabled. but you can still use it with memory store', ); return { tokens: {}, clients: {} }; } } async saveStorageData(data: StorageData): Promise<void> { if (!this.isInitializedStorageSuccess) { return; } await this.initialize(); try { const encryptedData = this.encrypt(JSON.stringify(data, null, 2)); fs.writeFileSync(this.storageFile, encryptedData); } catch (error) { logger.error(`[StorageManager] Failed to save storage data: ${error}`); throw error; } } } export const storageManager = new StorageManager();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/larksuite/lark-openapi-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server