Files
livedash-node/lib/database-pool.ts
Kaj Kowalski 38aff21c3a fix: comprehensive security and type improvements from PR #20 review
Security Enhancements:
- Implemented proper rate limiting with automatic cleanup for /register and /forgot-password endpoints
- Added memory usage protection with MAX_ENTRIES limit (10000)
- Fixed rate limiter memory leaks by adding cleanup intervals
- Improved IP extraction with x-real-ip and x-client-ip header support

Code Quality Improvements:
- Refactored ProcessingStatusManager from individual functions to class-based architecture
- Maintained backward compatibility with singleton instance pattern
- Fixed TypeScript strict mode violations across the codebase
- Resolved all build errors and type mismatches

UI Component Fixes:
- Removed unused chart components (Charts.tsx, DonutChart.tsx)
- Fixed calendar component type issues by removing unused custom implementations
- Resolved theme provider type imports
- Fixed confetti component default options handling
- Corrected pointer component coordinate type definitions

Type System Improvements:
- Extended NextAuth types to support dual auth systems (regular and platform users)
- Fixed nullable type handling throughout the codebase
- Resolved Prisma JSON field type compatibility issues
- Corrected SessionMessage and ImportRecord interface definitions
- Fixed ES2015 iteration compatibility issues

Database & Performance:
- Updated database pool configuration for Prisma adapter compatibility
- Fixed pagination response structure in user management endpoints
- Improved error handling with proper error class usage

Testing & Build:
- All TypeScript compilation errors resolved
- ESLint warnings remain but no errors
- Build completes successfully with proper static generation
2025-06-30 19:15:25 +02:00

144 lines
4.1 KiB
TypeScript

// Advanced database connection pooling configuration
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "@prisma/client";
import { Pool } from "pg";
import { env } from "./env";
// Enhanced connection pool configuration
const createConnectionPool = () => {
// Parse DATABASE_URL to get connection parameters
const databaseUrl = new URL(env.DATABASE_URL);
const pool = new Pool({
host: databaseUrl.hostname,
port: Number.parseInt(databaseUrl.port) || 5432,
user: databaseUrl.username,
password: databaseUrl.password,
database: databaseUrl.pathname.slice(1), // Remove leading slash
ssl: databaseUrl.searchParams.get("sslmode") !== "disable",
// Connection pool configuration
max: env.DATABASE_CONNECTION_LIMIT, // Maximum number of connections
min: 2, // Minimum number of connections to maintain
idleTimeoutMillis: env.DATABASE_POOL_TIMEOUT * 1000, // Close idle connections after timeout
connectionTimeoutMillis: 10000, // Connection timeout
maxUses: 1000, // Maximum uses per connection before cycling
allowExitOnIdle: true, // Allow process to exit when all connections are idle
// Health check configuration
query_timeout: 30000, // Query timeout
keepAlive: true,
keepAliveInitialDelayMillis: 30000,
});
// Connection pool event handlers
pool.on("connect", () => {
console.log(
`Database connection established. Active connections: ${pool.totalCount}`
);
});
pool.on("acquire", () => {
console.log(
`Connection acquired from pool. Waiting: ${pool.waitingCount}, Idle: ${pool.idleCount}`
);
});
pool.on("release", () => {
console.log(
`Connection released to pool. Active: ${pool.totalCount - pool.idleCount}, Idle: ${pool.idleCount}`
);
});
pool.on("error", (err) => {
console.error("Database pool error:", err);
});
pool.on("remove", () => {
console.log(
`Connection removed from pool. Total connections: ${pool.totalCount}`
);
});
return pool;
};
// Create adapter with connection pool
export const createEnhancedPrismaClient = () => {
// Parse DATABASE_URL to get connection parameters
const dbUrl = new URL(env.DATABASE_URL);
const poolConfig = {
host: dbUrl.hostname,
port: parseInt(dbUrl.port || "5432"),
database: dbUrl.pathname.slice(1), // Remove leading '/'
user: dbUrl.username,
password: decodeURIComponent(dbUrl.password),
ssl: dbUrl.searchParams.get("sslmode") !== "disable" ? { rejectUnauthorized: false } : undefined,
// Connection pool settings
max: 20, // Maximum number of connections
idleTimeoutMillis: 30000, // 30 seconds
connectionTimeoutMillis: 5000, // 5 seconds
query_timeout: 10000, // 10 seconds
statement_timeout: 10000, // 10 seconds
// Connection lifecycle
allowExitOnIdle: true,
};
const adapter = new PrismaPg(poolConfig);
return new PrismaClient({
adapter,
log:
process.env.NODE_ENV === "development"
? ["query", "info", "warn", "error"]
: ["error"],
});
};
// Connection pool monitoring utilities
export const getPoolStats = (pool: Pool) => {
return {
totalConnections: pool.totalCount,
idleConnections: pool.idleCount,
waitingQueries: pool.waitingCount,
activeConnections: pool.totalCount - pool.idleCount,
};
};
// Health check for the connection pool
export const checkPoolHealth = async (
pool: Pool
): Promise<{
healthy: boolean;
stats: ReturnType<typeof getPoolStats>;
error?: string;
}> => {
try {
const client = await pool.connect();
await client.query("SELECT 1");
client.release();
return {
healthy: true,
stats: getPoolStats(pool),
};
} catch (error) {
return {
healthy: false,
stats: getPoolStats(pool),
error: error instanceof Error ? error.message : String(error),
};
}
};
// Graceful pool shutdown
export const shutdownPool = async (pool: Pool) => {
console.log("Shutting down database connection pool...");
await pool.end();
console.log("Database connection pool shut down successfully.");
};