188 lines
6.7 KiB
TypeScript
188 lines
6.7 KiB
TypeScript
/**
|
||
* Управляет кешированием файлов (внутренних, внешних, промптов).
|
||
* Сохраняет файлы в указанной папке с UUID в имени.
|
||
*/
|
||
|
||
import { TFile, normalizePath } from 'obsidian';
|
||
import LLMAgentPlugin from 'main';
|
||
import { v4 as uuidv4 } from 'uuid'; // Нужно установить: npm install uuid @types/uuid
|
||
import { getMimeType } from 'src/utils/fileUtils';
|
||
|
||
export class CacheManager {
|
||
private plugin: LLMAgentPlugin;
|
||
|
||
constructor(plugin: LLMAgentPlugin) {
|
||
this.plugin = plugin;
|
||
}
|
||
|
||
/**
|
||
* Возвращает абсолютный путь к папке кеша
|
||
*/
|
||
public getCacheFolderPath(): string {
|
||
const cacheFolder = this.plugin.settings.refCacheFolder;
|
||
|
||
// Если путь относительный, делаем его относительно vault
|
||
if (!this.isAbsolutePath(cacheFolder)) {
|
||
return this.plugin.app.vault.adapter.getBasePath() + '/' + cacheFolder;
|
||
}
|
||
|
||
return cacheFolder;
|
||
}
|
||
|
||
/**
|
||
* Проверяет, является ли путь абсолютным
|
||
*/
|
||
private isAbsolutePath(path: string): boolean {
|
||
// Windows: C:\, D:\, и т.д.
|
||
if (/^[a-zA-Z]:\\/.test(path)) return true;
|
||
// Unix: /
|
||
if (path.startsWith('/')) return true;
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Создает папку кеша, если она не существует
|
||
*/
|
||
private async ensureCacheFolderExists(): Promise<void> {
|
||
const cachePath = this.getCacheFolderPath();
|
||
|
||
if (!window.require) return; // Web версия
|
||
|
||
const fs = window.require('fs').promises;
|
||
|
||
try {
|
||
await fs.access(cachePath);
|
||
} catch {
|
||
await fs.mkdir(cachePath, { recursive: true });
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Кеширует файл из vault
|
||
*/
|
||
async cacheVaultFile(file: TFile): Promise<{ uuid: string; cachedPath: string }> {
|
||
await this.ensureCacheFolderExists();
|
||
|
||
const uuid = uuidv4();
|
||
const extension = file.extension;
|
||
const cachedFileName = `${file.basename}-${uuid}.${extension}`;
|
||
|
||
const content = await this.plugin.app.vault.readBinary(file);
|
||
|
||
const cacheFolderPath = this.getCacheFolderPath();
|
||
const cachedFilePath = `${cacheFolderPath}/${cachedFileName}`;
|
||
|
||
if (!window.require) {
|
||
throw new Error('Кеширование доступно только в desktop версии');
|
||
}
|
||
|
||
const fs = window.require('fs').promises;
|
||
await fs.writeFile(cachedFilePath, Buffer.from(content));
|
||
|
||
return {
|
||
uuid: uuid,
|
||
cachedPath: cachedFileName // Относительный путь
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Кеширует внешний файл
|
||
*/
|
||
async cacheExternalFile(absolutePath: string): Promise<{ uuid: string; cachedPath: string; name: string; mimeType: string }> {
|
||
await this.ensureCacheFolderExists();
|
||
|
||
if (!window.require) {
|
||
throw new Error('Кеширование доступно только в desktop версии');
|
||
}
|
||
|
||
const fs = window.require('fs').promises;
|
||
const path = window.require('path');
|
||
|
||
const content = await fs.readFile(absolutePath);
|
||
const fileName = path.basename(absolutePath);
|
||
const extension = path.extname(fileName).substring(1);
|
||
const baseName = path.basename(fileName, path.extname(fileName));
|
||
|
||
const uuid = uuidv4();
|
||
const cachedFileName = `${baseName}-${uuid}.${extension}`;
|
||
|
||
const cacheFolderPath = this.getCacheFolderPath();
|
||
const cachedFilePath = `${cacheFolderPath}/${cachedFileName}`;
|
||
|
||
await fs.writeFile(cachedFilePath, content);
|
||
|
||
return {
|
||
uuid: uuid,
|
||
cachedPath: cachedFileName,
|
||
name: fileName,
|
||
mimeType: getMimeType(fileName)
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Восстанавливает кеш из исходного файла
|
||
*/
|
||
async restoreCache(sourcePath: string, type: 'file' | 'prompt' | 'external', uuid: string): Promise<string> {
|
||
if (type === 'external') {
|
||
// Для внешних файлов sourcePath - абсолютный
|
||
const result = await this.cacheExternalFile(sourcePath);
|
||
return result.cachedPath;
|
||
} else if (type === 'file') {
|
||
// Для внутренних файлов sourcePath - относительный
|
||
const file = this.plugin.app.vault.getAbstractFileByPath(sourcePath);
|
||
if (file instanceof TFile) {
|
||
const result = await this.cacheVaultFile(file);
|
||
return result.cachedPath;
|
||
}
|
||
} else if (type === 'prompt') {
|
||
// Для промптов
|
||
const promptsFolder = this.plugin.settings.promptsFolder;
|
||
const fullPath = this.isAbsolutePath(sourcePath)
|
||
? sourcePath
|
||
: `${promptsFolder}/${sourcePath}`;
|
||
|
||
const file = this.plugin.app.vault.getAbstractFileByPath(fullPath);
|
||
if (file instanceof TFile) {
|
||
const result = await this.cacheVaultFile(file);
|
||
return result.cachedPath;
|
||
}
|
||
}
|
||
|
||
throw new Error(`Не удалось восстановить кеш для ${sourcePath}`);
|
||
}
|
||
|
||
/**
|
||
* Читает содержимое из кеша
|
||
*/
|
||
async readCachedFile(cachedPath: string): Promise<ArrayBuffer> {
|
||
const cacheFolderPath = this.getCacheFolderPath();
|
||
const fullPath = `${cacheFolderPath}/${cachedPath}`;
|
||
|
||
if (!window.require) {
|
||
throw new Error('Чтение кеша доступно только в desktop версии');
|
||
}
|
||
|
||
const fs = window.require('fs').promises;
|
||
const buffer = await fs.readFile(fullPath);
|
||
return buffer.buffer;
|
||
}
|
||
|
||
/**
|
||
* Проверяет существование кешированного файла
|
||
*/
|
||
async cachedFileExists(cachedPath: string): Promise<boolean> {
|
||
const cacheFolderPath = this.getCacheFolderPath();
|
||
const fullPath = `${cacheFolderPath}/${cachedPath}`;
|
||
|
||
if (!window.require) return false;
|
||
|
||
const fs = window.require('fs').promises;
|
||
|
||
try {
|
||
await fs.access(fullPath);
|
||
return true;
|
||
} catch {
|
||
return false;
|
||
}
|
||
}
|
||
} |