First commit

This commit is contained in:
Sachin
2025-12-16 22:26:18 +05:30
commit 03ed187ebe
122 changed files with 68601 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
{
"name": "@core/storage",
"version": "1.0.0",
"description": "Core storage SDK for encrypted RxDB/SQLCipher database management",
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "nx build",
"test": "nx test",
"lint": "nx lint",
"typecheck": "nx typecheck"
},
"dependencies": {
"rxdb": "^15.0.0",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@types/node": "^20.0.0"
},
"peerDependencies": {
"@core/security": "workspace:*"
},
"nx": {
"tags": ["scope:core", "type:infra", "platform:rn"]
}
}

View File

@@ -0,0 +1,220 @@
import type { SecurityCore } from '@core/security';
import { DatabaseProvider } from './DatabaseProvider';
import { SchemaRegistry } from './SchemaRegistry';
import type { StorageConfig, CollectionSchema, DatabaseInstance } from './types';
/**
* Core Storage SDK
* Provides encrypted local database management with RxDB/SQLCipher
*
* Key Features (per FRD):
* - F.STC.001: Abstraction layer over RxDB/SQLite with SQLCipher encryption
* - F.STC.002: Schema registration for feature SDKs
* - F.STC.003: Conflict resolution strategies (CRDT configuration, LWW handling)
* - F.STC.004: Encrypted file vault for local media storage
* - F.STC.005: Automated database schema migration
* - F.STC.006: Incremental encryption re-keying
* - F.STC.007: Selective purge mechanisms
*/
export class StorageCore {
private config: StorageConfig;
private securityCore: SecurityCore;
private databaseProvider: DatabaseProvider;
private schemaRegistry: SchemaRegistry;
private initialized = false;
constructor(config: StorageConfig, securityCore: SecurityCore) {
this.config = config;
this.securityCore = securityCore;
this.databaseProvider = new DatabaseProvider(config, securityCore);
this.schemaRegistry = new SchemaRegistry();
}
/**
* F.STC.001: Initialize the storage layer with encryption
*/
async initialize(): Promise<void> {
if (this.initialized) return;
await this.databaseProvider.initialize();
this.initialized = true;
}
/**
* F.STC.002: Register schema for feature SDKs
*/
registerSchema(collectionName: string, schema: CollectionSchema): void {
this.schemaRegistry.register(collectionName, schema);
}
/**
* Get the database instance
*/
getDatabase(): DatabaseInstance {
this.ensureInitialized();
return this.databaseProvider.getInstance();
}
/**
* F.STC.002: Create collection with registered schema
*/
async createCollection(name: string): Promise<void> {
this.ensureInitialized();
const schema = this.schemaRegistry.getSchema(name);
if (!schema) {
throw new Error(`Schema not registered for collection: ${name}`);
}
await this.databaseProvider.createCollection(name, schema);
}
/**
* F.STC.004: Store encrypted file
*/
async storeEncryptedFile(
filename: string,
data: Buffer,
mimeType: string,
orgId?: string
): Promise<string> {
this.ensureInitialized();
// Encrypt file content
const encryptResult = orgId
? await this.securityCore.encryptForOrganization(data.toString('base64'), orgId)
: await this.encryptFileLocally(data);
// Store encrypted file to filesystem
const encryptedPath = await this.writeEncryptedFile(filename, encryptResult.encryptedData);
// Store metadata in database
const fileId = this.generateFileId();
const metadata = {
id: fileId,
filename,
mimeType,
size: data.length,
encryptedPath,
keyReference: JSON.stringify(encryptResult.keyReference),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
const db = this.getDatabase();
await db.collections.fileVault.insert(metadata);
return fileId;
}
/**
* F.STC.004: Retrieve encrypted file
*/
async retrieveEncryptedFile(fileId: string, orgId?: string): Promise<Buffer> {
this.ensureInitialized();
const db = this.getDatabase();
const metadata = await db.collections.fileVault.findOne(fileId).exec();
if (!metadata) {
throw new Error(`File not found: ${fileId}`);
}
// Read encrypted file
const encryptedData = await this.readEncryptedFile(metadata.encryptedPath);
const keyReference = JSON.parse(metadata.keyReference);
// Decrypt file content
const decryptedData = orgId
? await this.securityCore.decryptForOrganization(encryptedData, keyReference)
: await this.decryptFileLocally(encryptedData, keyReference);
return Buffer.from(decryptedData, 'base64');
}
/**
* F.STC.007: Selective purge by organization
*/
async purgeOrganizationData(orgId: string): Promise<void> {
this.ensureInitialized();
const db = this.getDatabase();
// Find all collections and purge org-specific data
for (const [collectionName, collection] of Object.entries(db.collections)) {
// Remove documents with matching orgId
await collection.find({ orgId }).remove();
}
// Purge encrypted files belonging to organization
const orgFiles = await db.collections.fileVault
.find()
.where('keyReference')
.regex(new RegExp(orgId))
.exec();
for (const file of orgFiles) {
await this.deleteEncryptedFile(file.encryptedPath);
await file.remove();
}
}
/**
* F.STC.006: Re-encrypt data with new keys
*/
async reEncryptData(orgId: string, newKeyReference: any): Promise<void> {
this.ensureInitialized();
// This would implement re-encryption of existing data
// when organizational keys are rotated
console.warn('Re-encryption implementation needed');
}
/**
* Get sync status for collections
*/
getSyncStatus(collectionName: string): any {
this.ensureInitialized();
const db = this.getDatabase();
return db.collections[collectionName]?.find({ syncStatus: 'PENDING' });
}
private async encryptFileLocally(data: Buffer): Promise<any> {
// Implementation would use local encryption for non-org files
throw new Error('Local file encryption not implemented');
}
private async decryptFileLocally(encryptedData: string, keyReference: any): Promise<string> {
// Implementation would use local decryption for non-org files
throw new Error('Local file decryption not implemented');
}
private async writeEncryptedFile(filename: string, encryptedData: string): Promise<string> {
// Implementation would write to secure app documents directory
const path = `/secure/${this.generateFileId()}_${filename}`;
console.warn('File system integration needed');
return path;
}
private async readEncryptedFile(path: string): Promise<string> {
// Implementation would read from secure app documents directory
console.warn('File system integration needed');
return 'mock-encrypted-data';
}
private async deleteEncryptedFile(path: string): Promise<void> {
// Implementation would securely delete file
console.warn('Secure file deletion needed');
}
private generateFileId(): string {
return `file_${Date.now()}_${Math.random().toString(36).substring(2)}`;
}
private ensureInitialized(): void {
if (!this.initialized) {
throw new Error('StorageCore must be initialized before use');
}
}
}

View File

@@ -0,0 +1,9 @@
export { StorageCore } from './StorageCore';
export { DatabaseProvider } from './DatabaseProvider';
export { SchemaRegistry } from './SchemaRegistry';
export type {
StorageConfig,
CollectionSchema,
MigrationStrategy,
ConflictResolutionStrategy
} from './types';

View File

@@ -0,0 +1,42 @@
import type { RxDatabase, RxCollection } from 'rxdb';
export interface StorageConfig {
databaseName: string;
enableEncryption: boolean;
enableCRDT: boolean;
migrationStrategy: 'drop' | 'migrate';
}
export interface CollectionSchema {
version: number;
title: string;
type: 'object';
properties: Record<string, any>;
required?: string[];
indexes?: string[];
migrationStrategies?: Record<number, MigrationStrategy>;
conflictResolution?: ConflictResolutionStrategy;
}
export type MigrationStrategy = (oldDoc: any) => any;
export interface ConflictResolutionStrategy {
type: 'crdt' | 'lww' | 'custom';
resolver?: (conflicts: any[]) => any;
}
export interface DatabaseInstance {
database: RxDatabase;
collections: Record<string, RxCollection>;
}
export interface EncryptedFileMetadata {
id: string;
filename: string;
mimeType: string;
size: number;
encryptedPath: string;
keyReference: string;
createdAt: string;
updatedAt: string;
}