@escher-dbai/rag-module
Version:
Enterprise RAG module with chat context storage, vector search, and session management. Complete chat history retrieval and streaming content extraction for Electron apps.
142 lines (119 loc) • 3.92 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
/**
* Mapping Service - Handles ARN to anonymous ID mapping
*/
class MappingService {
constructor(basePath, encryptionService) {
this.basePath = basePath;
this.encryptionService = encryptionService;
this.mappingsPath = path.join(basePath, 'data', 'mappings.encrypted');
// In-memory cache
this.mappings = new Map(); // realId -> anonymousId
this.reverseMappings = new Map(); // anonymousId -> realId
this.loaded = false;
}
/**
* Load mappings from encrypted storage
*/
async loadMappings() {
try {
if (await fs.pathExists(this.mappingsPath)) {
const encryptedData = await fs.readFile(this.mappingsPath, 'utf8');
const decryptedData = await this.encryptionService.decryptForSync(encryptedData);
const mappingsData = JSON.parse(decryptedData);
this.mappings.clear();
this.reverseMappings.clear();
for (const [realId, anonymousId] of Object.entries(mappingsData)) {
this.mappings.set(realId, anonymousId);
this.reverseMappings.set(anonymousId, realId);
}
}
this.loaded = true;
} catch (error) {
// If loading fails, start with empty mappings
this.mappings.clear();
this.reverseMappings.clear();
this.loaded = true;
}
}
/**
* Save mappings to encrypted storage
*/
async saveMappings() {
const mappingsData = Object.fromEntries(this.mappings);
const encryptedData = await this.encryptionService.encryptForSync(JSON.stringify(mappingsData));
await fs.ensureDir(path.dirname(this.mappingsPath));
await fs.writeFile(this.mappingsPath, encryptedData);
}
/**
* Create mapping for a resource
*/
async createMapping(realId, metadata = {}) {
if (!this.loaded) await this.loadMappings();
if (!this.mappings.has(realId)) {
const anonymousId = this._generateAnonymousId(realId, metadata);
this.mappings.set(realId, anonymousId);
this.reverseMappings.set(anonymousId, realId);
await this.saveMappings();
}
return this.mappings.get(realId);
}
/**
* Get anonymous ID for real ID
*/
async getAnonymousId(realId) {
if (!this.loaded) await this.loadMappings();
return this.mappings.get(realId) || null;
}
/**
* Get real ID from anonymous ID (internal use only)
*/
async getRealId(anonymousId) {
if (!this.loaded) await this.loadMappings();
return this.reverseMappings.get(anonymousId) || null;
}
/**
* Remove mapping
*/
async removeMapping(realId) {
if (!this.loaded) await this.loadMappings();
const anonymousId = this.mappings.get(realId);
if (anonymousId) {
this.mappings.delete(realId);
this.reverseMappings.delete(anonymousId);
await this.saveMappings();
}
}
/**
* Get encrypted mapping data for backend service
*/
async getEncryptedMapping(resourceIds) {
if (!this.loaded) await this.loadMappings();
const mappingSubset = {};
for (const resourceId of resourceIds) {
const realId = await this.getRealId(resourceId) || resourceId;
if (this.mappings.has(realId)) {
mappingSubset[resourceId] = realId;
}
}
return await this.encryptionService.encryptForSync(JSON.stringify(mappingSubset));
}
/**
* Sync mappings to backend service
*/
async syncToBackend(config) {
// This would implement the actual backend sync
// For now, just return success
return true;
}
/**
* Generate anonymous ID
*/
_generateAnonymousId(realId, metadata = {}) {
const crypto = require('crypto');
const hash = crypto.createHash('sha256').update(realId + JSON.stringify(metadata)).digest('hex');
return 'res-' + hash.substring(0, 16);
}
}
module.exports = MappingService;