import { createServiceLogger } from '../../utils/logger'; import { ErrorCode, StoreType, FileUploadResult, FileResult } from '../types'; import { DBError } from '../core/error'; import { openStore } from './baseStore'; import { getHelia } from '../../ipfs/ipfsService'; import { CreateResult, StoreOptions } from '../types'; async function readAsyncIterableToBuffer( asyncIterable: AsyncIterable, ): Promise { const chunks: Uint8Array[] = []; for await (const chunk of asyncIterable) { chunks.push(chunk); } return Buffer.concat(chunks); } const logger = createServiceLogger('FILE_STORE'); /** * Upload a file to IPFS */ export const uploadFile = async ( fileData: Buffer, options?: { filename?: string; connectionId?: string; metadata?: Record; }, ): Promise => { try { const ipfs = getHelia(); if (!ipfs) { throw new DBError(ErrorCode.OPERATION_FAILED, 'IPFS instance not available'); } // Add to IPFS const unixfs = await import('@helia/unixfs'); const fs = unixfs.unixfs(ipfs); const cid = await fs.addBytes(fileData); const cidStr = cid.toString(); // Store metadata const filesDb = await openStore('_files', StoreType.KEYVALUE); await filesDb.put(cidStr, { filename: options?.filename, size: fileData.length, uploadedAt: Date.now(), ...options?.metadata, }); logger.info(`Uploaded file with CID: ${cidStr}`); return { cid: cidStr }; } catch (error: unknown) { if (error instanceof DBError) { throw error; } logger.error('Error uploading file:', error); throw new DBError(ErrorCode.OPERATION_FAILED, 'Failed to upload file', error); } }; /** * Get a file from IPFS by CID */ export const getFile = async (cid: string): Promise => { try { const ipfs = getHelia(); if (!ipfs) { throw new DBError(ErrorCode.OPERATION_FAILED, 'IPFS instance not available'); } // Get from IPFS const unixfs = await import('@helia/unixfs'); const fs = unixfs.unixfs(ipfs); const { CID } = await import('multiformats/cid'); const resolvedCid = CID.parse(cid); try { // Convert AsyncIterable to Buffer const bytes = await readAsyncIterableToBuffer(fs.cat(resolvedCid)); // Get metadata if available let metadata = null; try { const filesDb = await openStore('_files', StoreType.KEYVALUE); metadata = await filesDb.get(cid); } catch (_err) { // Metadata might not exist, continue without it } return { data: bytes, metadata }; } catch (error) { throw new DBError(ErrorCode.FILE_NOT_FOUND, `File with CID ${cid} not found`, error); } } catch (error: unknown) { if (error instanceof DBError) { throw error; } logger.error(`Error getting file with CID ${cid}:`, error); throw new DBError(ErrorCode.OPERATION_FAILED, `Failed to get file with CID ${cid}`, error); } }; /** * Delete a file from IPFS by CID */ export const deleteFile = async (cid: string): Promise => { try { // Delete metadata try { const filesDb = await openStore('_files', StoreType.KEYVALUE); await filesDb.del(cid); } catch (_err) { // Ignore if metadata doesn't exist } logger.info(`Deleted file with CID: ${cid}`); return true; } catch (error: unknown) { if (error instanceof DBError) { throw error; } logger.error(`Error deleting file with CID ${cid}:`, error); throw new DBError(ErrorCode.OPERATION_FAILED, `Failed to delete file with CID ${cid}`, error); } }; export const create = async >( collection: string, id: string, data: Omit, options?: StoreOptions, ): Promise => { try { const db = await openStore(collection, StoreType.KEYVALUE, options); // Prepare document for storage with ID // const document = { // id, // ...prepareDocument(collection, data) // }; const document = { id, ...data }; // Add to database const hash = await db.add(document); // Emit change event // events.emit('document:created', { collection, id, document, hash }); logger.info(`Created entry in file ${collection} with id ${id} and hash ${hash}`); return { id, hash }; } catch (error: unknown) { if (error instanceof DBError) { throw error; } logger.error(`Error creating entry in file ${collection}:`, error); throw new DBError( ErrorCode.OPERATION_FAILED, `Failed to create entry in file ${collection}`, error, ); } };