Skip to main content
Glama

Vextra MCP Server

by kcaitech
AGPL 3.0
3
file-cleanup.ts7.35 kB
import { getStorage } from "@/services/storage"; class FileCleanupService { private cleanupInterval: NodeJS.Timeout | null = null; private readonly EXPIRY_DAYS = 3; // 文件保留3天,防止在日期交替时删除了刚生成的图片 constructor() { this.startCleanupScheduler(); } /** * 生成文件路径 * 格式: images/{date}/{sessionId}/{fileName} */ generateFilePath(time: number, sessionId: string, fileName: string): string { const date = new Date(time).toISOString().split('T')[0]; // YYYY-MM-DD return `images/${date}/${sessionId}/${fileName}`; } /** * 清理过期的文件 * 基于OSS文件路径中的日期进行清理 */ async cleanupExpiredFiles(): Promise<void> { try { // 获取所有图片文件路径 const expiredFiles = await this.findExpiredFiles(); if (expiredFiles.length === 0) { console.log("No expired files found"); return; } // 删除过期文件 let deletedCount = 0; for (const filePath of expiredFiles) { try { await this.deleteFile(filePath); deletedCount++; } catch (error) { console.error(`Failed to delete file ${filePath}:`, error); } } console.log(`Cleaned up ${deletedCount} expired files out of ${expiredFiles.length} found`); } catch (error) { console.error("Error during file cleanup:", error); } } /** * 查找过期的文件 * 通过按日期前缀查找,避免一次性加载所有文件 */ private async findExpiredFiles(): Promise<string[]> { const storage = getStorage(); const expiredFiles: string[] = []; try { // 计算开始查找日期:当前日期 - EXPIRY_DAYS天 - 额外缓冲天数 const startDate = new Date(); startDate.setDate(startDate.getDate() - this.EXPIRY_DAYS - this.EXPIRY_DAYS); // 额外往前EXPIRY_DAYS天,确保不遗漏 const expiryDate = new Date(); expiryDate.setDate(expiryDate.getDate() - this.EXPIRY_DAYS); const expiryDateStr = expiryDate.toISOString().split('T')[0]; // YYYY-MM-DD console.log(`Searching files from ${startDate.toISOString().split('T')[0]} to ${expiryDateStr}`); // 按日期遍历,只查找可能包含过期文件的日期文件夹 const currentDate = new Date(startDate); let totalFilesFound = 0; while (currentDate <= expiryDate) { const dateStr = currentDate.toISOString().split('T')[0]; const prefix = `images/${dateStr}/`; try { const files = await storage.list(prefix); if (files.length > 0) { console.log(`Found ${files.length} files for date: ${dateStr}`); expiredFiles.push(...files); totalFilesFound += files.length; } } catch (error) { // 该日期文件夹不存在,跳过 console.log(`No files found for date: ${dateStr}`); } currentDate.setDate(currentDate.getDate() + 1); } console.log(`Found ${expiredFiles.length} expired files from ${totalFilesFound} total files in date range`); } catch (error) { console.error("Error finding expired files:", error); } return expiredFiles; } /** * 删除单个文件 */ private async deleteFile(filePath: string): Promise<void> { const storage = getStorage(); try { await storage.delete(filePath); console.log(`Successfully deleted file: ${filePath}`); } catch (error) { console.error(`Failed to delete file ${filePath}:`, error); throw error; } } /** * 清理指定会话的所有文件 * 通过OSS路径模式查找并删除 */ async cleanupSessionFiles(time: number, sessionId: string): Promise<void> { try { console.log(`Cleaning up files for session: ${sessionId}`); // 查找该会话的所有文件 const sessionFiles = await this.findSessionFiles(time, sessionId); if (sessionFiles.length === 0) { console.log(`No files found for session ${sessionId}`); return; } // 删除会话文件 let deletedCount = 0; for (const filePath of sessionFiles) { try { await this.deleteFile(filePath); deletedCount++; } catch (error) { console.error(`Failed to delete session file ${filePath}:`, error); } } console.log(`Cleaned up ${deletedCount} files for session ${sessionId}`); } catch (error) { console.error(`Error cleaning up session ${sessionId}:`, error); } } /** * 查找指定会话的所有文件 */ private async findSessionFiles(time: number, sessionId: string): Promise<string[]> { const storage = getStorage(); try { console.log(`Looking for files with session: ${sessionId}`); const date = new Date(time).toISOString().split('T')[0]; // YYYY-MM-DD // 获取所有images/前缀的文件 const allFiles = await storage.list(`images/${date}/`); // 过滤出指定会话的文件 const sessionFiles = allFiles.filter(file => { const parts = file.split('/'); if (parts.length >= 3) { const fileSessionId = parts[2]; // images/date/sessionId/filename return fileSessionId === sessionId; } return false; }); console.log(`Found ${sessionFiles.length} files for session ${sessionId}`); return sessionFiles; } catch (error) { console.error(`Error finding session files for ${sessionId}:`, error); return []; } } /** * 启动清理调度器 */ private startCleanupScheduler(): void { // 每小时运行一次清理 this.cleanupInterval = setInterval(async () => { await this.cleanupExpiredFiles(); }, 60 * 60 * 1000); // 1小时 console.log("File cleanup scheduler started (every hour)"); } /** * 停止清理调度器 */ stopCleanupScheduler(): void { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; console.log("File cleanup scheduler stopped"); } } } // 创建全局实例 export const fileCleanupService = new FileCleanupService(); // 导出清理会话文件的函数,供其他地方使用 export async function cleanupSessionFiles(time: number, sessionId: string): Promise<void> { await fileCleanupService.cleanupSessionFiles(time, sessionId); }

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/kcaitech/vextra-mcp'

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