Spaces:
Paused
Paused
File size: 6,028 Bytes
34367da | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | /**
* Prisma Database Adapter
*
* Production-grade database adapter using Prisma ORM.
* As of Prisma 5.10.0+, ARM64 Windows is fully supported.
*/
import { logger } from '../../utils/logger.js';
// Type definitions
export type PrismaClient = any;
/**
* Check if PostgreSQL is configured
*/
function hasPostgresConfig(): boolean {
return !!process.env.DATABASE_URL;
}
/**
* Database Adapter Interface
* Generic interface for database operations
*/
export interface DatabaseAdapter {
initialize(): Promise<void>;
disconnect(): Promise<void>;
query(sql: string, params?: any[]): Promise<any>;
execute(sql: string, params?: any[]): Promise<number>;
transaction<T>(fn: (tx: any) => Promise<T>): Promise<T>;
isAvailable(): boolean;
}
/**
* Stub adapter for when Prisma is unavailable
* Returns graceful no-ops and uses SQLite as fallback
*/
class StubDatabaseAdapter implements DatabaseAdapter {
private reason: string;
constructor(reason: string) {
this.reason = reason;
}
async initialize(): Promise<void> {
logger.info(`ℹ️ PostgreSQL skipped: ${this.reason}`);
logger.info(' System will use SQLite + Neo4j (fully functional)');
}
async disconnect(): Promise<void> {
// No-op
}
async query(_sql: string, _params?: any[]): Promise<any> {
throw new Error(`PostgreSQL not available: ${this.reason}. Use SQLite for local operations.`);
}
async transaction<T>(_fn: (tx: any) => Promise<T>): Promise<T> {
throw new Error(`PostgreSQL not available: ${this.reason}. Use SQLite for local operations.`);
}
async execute(_sql: string, _params?: any[]): Promise<number> {
throw new Error(`PostgreSQL not available: ${this.reason}. Use SQLite for local operations.`);
}
isAvailable(): boolean {
return false;
}
getClient(): null {
return null;
}
}
/**
* Prisma Database Adapter
* Production-grade database adapter using Prisma ORM
*
* Only loads Prisma when:
* 1. NOT on Windows ARM64 (no native binaries available)
* 2. DATABASE_URL is configured
*/
class PrismaDatabaseAdapterImpl implements DatabaseAdapter {
private prisma: any = null;
private isInitialized = false;
private loadError: string | null = null;
constructor() {
// Prisma loading is deferred to initialize()
}
async initialize(): Promise<void> {
if (this.isInitialized) return;
try {
// Dynamic import to defer binary loading
const { PrismaClient } = await import('@prisma/client');
this.prisma = new PrismaClient({
log: process.env.NODE_ENV === 'development'
? ['info', 'warn', 'error']
: ['error'],
});
await this.prisma.$connect();
logger.info('🗄️ Prisma Database connected to PostgreSQL');
// Enable pgvector extension if available (optional - we use Neo4j for vectors now)
try {
await this.prisma.$executeRaw`CREATE EXTENSION IF NOT EXISTS vector`;
logger.info('🔢 pgvector extension enabled');
} catch (_extError: any) {
// Non-critical - Railway PostgreSQL doesn't have pgvector
// We use Neo4j as primary vector store instead
logger.info('ℹ️ pgvector not available (using Neo4j for vector operations)');
}
this.isInitialized = true;
} catch (error: any) {
this.loadError = error.message;
logger.warn('⚠️ Prisma/PostgreSQL initialization failed:', { error: error.message });
logger.info(' Continuing with SQLite + Neo4j (fully functional)');
// Don't throw - allow graceful fallback
}
}
async disconnect(): Promise<void> {
if (this.prisma && this.isInitialized) {
await this.prisma.$disconnect();
logger.info('🗄️ Prisma Database disconnected');
}
}
async query(sql: string, params?: any[]): Promise<any> {
if (!this.prisma || !this.isInitialized) {
throw new Error('PostgreSQL not connected. Use SQLite for local operations.');
}
return this.prisma.$queryRawUnsafe(sql, ...(params || []));
}
async execute(sql: string, params?: any[]): Promise<number> {
if (!this.prisma || !this.isInitialized) {
throw new Error('PostgreSQL not connected. Use SQLite for local operations.');
}
return this.prisma.$executeRawUnsafe(sql, ...(params || []));
}
async transaction<T>(fn: (tx: any) => Promise<T>): Promise<T> {
if (!this.prisma || !this.isInitialized) {
throw new Error('PostgreSQL not connected. Use SQLite for local operations.');
}
return this.prisma.$transaction(fn);
}
isAvailable(): boolean {
return this.isInitialized && this.prisma !== null;
}
getClient(): any {
return this.prisma;
}
}
// Singleton instance
let dbInstance: DatabaseAdapter | null = null;
/**
* Get the database adapter (singleton)
*
* Returns:
* - StubAdapter if DATABASE_URL is not configured
* - PrismaDatabaseAdapterImpl otherwise (supports all platforms including ARM64 Windows)
*/
export function getDatabaseAdapter(): DatabaseAdapter & { getClient(): any } {
if (!dbInstance) {
if (!hasPostgresConfig()) {
dbInstance = new StubDatabaseAdapter('DATABASE_URL not configured');
} else {
dbInstance = new PrismaDatabaseAdapterImpl();
}
}
return dbInstance as DatabaseAdapter & { getClient(): any };
}
// Export type alias for compatibility
export { PrismaDatabaseAdapterImpl as PrismaDatabaseAdapter };
|