mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 19:52:09 +01:00
refactor: fix biome linting issues and update project documentation
- Fix 36+ biome linting issues reducing errors/warnings from 227 to 191 - Replace explicit 'any' types with proper TypeScript interfaces - Fix React hooks dependencies and useCallback patterns - Resolve unused variables and parameter assignment issues - Improve accessibility with proper label associations - Add comprehensive API documentation for admin and security features - Update README.md with accurate PostgreSQL setup and current tech stack - Create complete documentation for audit logging, CSP monitoring, and batch processing - Fix outdated project information and missing developer workflows
This commit is contained in:
@ -44,24 +44,83 @@ export class PreDeploymentChecker {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
migrationLogger.startPhase("PRE_DEPLOYMENT", "Running pre-deployment validation checks");
|
||||
migrationLogger.startPhase(
|
||||
"PRE_DEPLOYMENT",
|
||||
"Running pre-deployment validation checks"
|
||||
);
|
||||
|
||||
// Define all checks to run
|
||||
const checkSuite = [
|
||||
{ name: "Environment Configuration", fn: () => this.checkEnvironmentConfiguration(), critical: true },
|
||||
{ name: "Database Connection", fn: () => this.checkDatabaseConnection(), critical: true },
|
||||
{ name: "Database Schema", fn: () => this.checkDatabaseSchema(), critical: true },
|
||||
{ name: "Database Data Integrity", fn: () => this.checkDataIntegrity(), critical: true },
|
||||
{ name: "Dependencies", fn: () => this.checkDependencies(), critical: true },
|
||||
{ name: "File System Permissions", fn: () => this.checkFileSystemPermissions(), critical: false },
|
||||
{ name: "Port Availability", fn: () => this.checkPortAvailability(), critical: true },
|
||||
{ name: "OpenAI API Access", fn: () => this.checkOpenAIAccess(), critical: true },
|
||||
{ name: "tRPC Infrastructure", fn: () => this.checkTRPCInfrastructure(), critical: true },
|
||||
{ name: "Batch Processing Readiness", fn: () => this.checkBatchProcessingReadiness(), critical: true },
|
||||
{ name: "Security Configuration", fn: () => this.checkSecurityConfiguration(), critical: false },
|
||||
{ name: "Performance Configuration", fn: () => this.checkPerformanceConfiguration(), critical: false },
|
||||
{ name: "Backup Validation", fn: () => this.checkBackupValidation(), critical: false },
|
||||
{ name: "Migration Rollback Readiness", fn: () => this.checkRollbackReadiness(), critical: false },
|
||||
{
|
||||
name: "Environment Configuration",
|
||||
fn: () => this.checkEnvironmentConfiguration(),
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: "Database Connection",
|
||||
fn: () => this.checkDatabaseConnection(),
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: "Database Schema",
|
||||
fn: () => this.checkDatabaseSchema(),
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: "Database Data Integrity",
|
||||
fn: () => this.checkDataIntegrity(),
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: "Dependencies",
|
||||
fn: () => this.checkDependencies(),
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: "File System Permissions",
|
||||
fn: () => this.checkFileSystemPermissions(),
|
||||
critical: false,
|
||||
},
|
||||
{
|
||||
name: "Port Availability",
|
||||
fn: () => this.checkPortAvailability(),
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: "OpenAI API Access",
|
||||
fn: () => this.checkOpenAIAccess(),
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: "tRPC Infrastructure",
|
||||
fn: () => this.checkTRPCInfrastructure(),
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: "Batch Processing Readiness",
|
||||
fn: () => this.checkBatchProcessingReadiness(),
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: "Security Configuration",
|
||||
fn: () => this.checkSecurityConfiguration(),
|
||||
critical: false,
|
||||
},
|
||||
{
|
||||
name: "Performance Configuration",
|
||||
fn: () => this.checkPerformanceConfiguration(),
|
||||
critical: false,
|
||||
},
|
||||
{
|
||||
name: "Backup Validation",
|
||||
fn: () => this.checkBackupValidation(),
|
||||
critical: false,
|
||||
},
|
||||
{
|
||||
name: "Migration Rollback Readiness",
|
||||
fn: () => this.checkRollbackReadiness(),
|
||||
critical: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Run all checks
|
||||
@ -70,8 +129,13 @@ export class PreDeploymentChecker {
|
||||
}
|
||||
|
||||
const totalDuration = Date.now() - startTime;
|
||||
const criticalFailures = this.checks.filter(c => c.critical && !c.success).length;
|
||||
const warningCount = this.checks.reduce((sum, c) => sum + c.warnings.length, 0);
|
||||
const criticalFailures = this.checks.filter(
|
||||
(c) => c.critical && !c.success
|
||||
).length;
|
||||
const warningCount = this.checks.reduce(
|
||||
(sum, c) => sum + c.warnings.length,
|
||||
0
|
||||
);
|
||||
|
||||
const result: PreDeploymentResult = {
|
||||
success: criticalFailures === 0,
|
||||
@ -84,13 +148,19 @@ export class PreDeploymentChecker {
|
||||
if (result.success) {
|
||||
migrationLogger.completePhase("PRE_DEPLOYMENT");
|
||||
} else {
|
||||
migrationLogger.error("PRE_DEPLOYMENT", `Pre-deployment checks failed with ${criticalFailures} critical failures`);
|
||||
migrationLogger.error(
|
||||
"PRE_DEPLOYMENT",
|
||||
`Pre-deployment checks failed with ${criticalFailures} critical failures`
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
} catch (error) {
|
||||
migrationLogger.error("PRE_DEPLOYMENT", "Pre-deployment check suite failed", error as Error);
|
||||
migrationLogger.error(
|
||||
"PRE_DEPLOYMENT",
|
||||
"Pre-deployment check suite failed",
|
||||
error as Error
|
||||
);
|
||||
throw error;
|
||||
} finally {
|
||||
await this.prisma.$disconnect();
|
||||
@ -99,7 +169,7 @@ export class PreDeploymentChecker {
|
||||
|
||||
private async runSingleCheck(
|
||||
name: string,
|
||||
checkFn: () => Promise<Omit<CheckResult, 'name' | 'duration'>>,
|
||||
checkFn: () => Promise<Omit<CheckResult, "name" | "duration">>,
|
||||
critical: boolean
|
||||
): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
@ -120,20 +190,29 @@ export class PreDeploymentChecker {
|
||||
this.checks.push(checkResult);
|
||||
|
||||
if (result.success) {
|
||||
migrationLogger.info("CHECK", `✅ ${name} passed`, { duration, warnings: result.warnings.length });
|
||||
migrationLogger.info("CHECK", `✅ ${name} passed`, {
|
||||
duration,
|
||||
warnings: result.warnings.length,
|
||||
});
|
||||
} else {
|
||||
const level = critical ? "ERROR" : "WARN";
|
||||
migrationLogger[level.toLowerCase() as 'error' | 'warn']("CHECK", `❌ ${name} failed`, undefined, {
|
||||
errors: result.errors.length,
|
||||
warnings: result.warnings.length,
|
||||
duration
|
||||
});
|
||||
migrationLogger[level.toLowerCase() as "error" | "warn"](
|
||||
"CHECK",
|
||||
`❌ ${name} failed`,
|
||||
undefined,
|
||||
{
|
||||
errors: result.errors.length,
|
||||
warnings: result.warnings.length,
|
||||
duration,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (result.warnings.length > 0) {
|
||||
migrationLogger.warn("CHECK", `${name} has warnings`, { warnings: result.warnings });
|
||||
migrationLogger.warn("CHECK", `${name} has warnings`, {
|
||||
warnings: result.warnings,
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
const duration = Date.now() - startTime;
|
||||
const checkResult: CheckResult = {
|
||||
@ -146,11 +225,15 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
|
||||
this.checks.push(checkResult);
|
||||
migrationLogger.error("CHECK", `💥 ${name} crashed`, error as Error, { duration });
|
||||
migrationLogger.error("CHECK", `💥 ${name} crashed`, error as Error, {
|
||||
duration,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async checkEnvironmentConfiguration(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkEnvironmentConfiguration(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -163,9 +246,9 @@ export class PreDeploymentChecker {
|
||||
|
||||
// Additional environment checks
|
||||
const requiredVars = [
|
||||
'DATABASE_URL',
|
||||
'NEXTAUTH_SECRET',
|
||||
'OPENAI_API_KEY'
|
||||
"DATABASE_URL",
|
||||
"NEXTAUTH_SECRET",
|
||||
"OPENAI_API_KEY",
|
||||
];
|
||||
|
||||
for (const varName of requiredVars) {
|
||||
@ -175,17 +258,13 @@ export class PreDeploymentChecker {
|
||||
}
|
||||
|
||||
// Check new variables
|
||||
const newVars = [
|
||||
'BATCH_PROCESSING_ENABLED',
|
||||
'TRPC_ENDPOINT_URL'
|
||||
];
|
||||
const newVars = ["BATCH_PROCESSING_ENABLED", "TRPC_ENDPOINT_URL"];
|
||||
|
||||
for (const varName of newVars) {
|
||||
if (!process.env[varName]) {
|
||||
warnings.push(`New environment variable not set: ${varName}`);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
errors.push(`Environment validation failed: ${(error as Error).message}`);
|
||||
}
|
||||
@ -197,7 +276,9 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkDatabaseConnection(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkDatabaseConnection(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -215,7 +296,6 @@ export class PreDeploymentChecker {
|
||||
if (connections.length !== 3) {
|
||||
warnings.push("Connection pooling may have issues");
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
errors.push(`Database connection failed: ${(error as Error).message}`);
|
||||
}
|
||||
@ -227,7 +307,9 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkDatabaseSchema(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkDatabaseSchema(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const validator = new DatabaseValidator();
|
||||
|
||||
try {
|
||||
@ -247,7 +329,9 @@ export class PreDeploymentChecker {
|
||||
}
|
||||
}
|
||||
|
||||
private async checkDataIntegrity(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkDataIntegrity(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -257,11 +341,13 @@ export class PreDeploymentChecker {
|
||||
const importCount = await this.prisma.sessionImport.count();
|
||||
|
||||
if (sessionCount === 0 && importCount === 0) {
|
||||
warnings.push("No session data found - this may be a fresh installation");
|
||||
warnings.push(
|
||||
"No session data found - this may be a fresh installation"
|
||||
);
|
||||
}
|
||||
|
||||
// Check for orphaned processing status records
|
||||
const orphanedStatus = await this.prisma.$queryRaw<{count: bigint}[]>`
|
||||
const orphanedStatus = await this.prisma.$queryRaw<{ count: bigint }[]>`
|
||||
SELECT COUNT(*) as count
|
||||
FROM "SessionProcessingStatus" sps
|
||||
LEFT JOIN "Session" s ON sps."sessionId" = s.id
|
||||
@ -269,9 +355,10 @@ export class PreDeploymentChecker {
|
||||
`;
|
||||
|
||||
if (orphanedStatus[0]?.count > 0) {
|
||||
warnings.push(`Found ${orphanedStatus[0].count} orphaned processing status records`);
|
||||
warnings.push(
|
||||
`Found ${orphanedStatus[0].count} orphaned processing status records`
|
||||
);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
errors.push(`Data integrity check failed: ${(error as Error).message}`);
|
||||
}
|
||||
@ -283,7 +370,9 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkDependencies(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkDependencies(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -307,19 +396,21 @@ export class PreDeploymentChecker {
|
||||
];
|
||||
|
||||
for (const dep of requiredDeps) {
|
||||
if (!packageJson.dependencies?.[dep] && !packageJson.devDependencies?.[dep]) {
|
||||
if (
|
||||
!packageJson.dependencies?.[dep] &&
|
||||
!packageJson.devDependencies?.[dep]
|
||||
) {
|
||||
errors.push(`Missing required dependency: ${dep}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Node.js version
|
||||
const nodeVersion = process.version;
|
||||
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
|
||||
const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0]);
|
||||
|
||||
if (majorVersion < 18) {
|
||||
errors.push(`Node.js ${nodeVersion} is too old. Requires Node.js 18+`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
errors.push(`Dependency check failed: ${(error as Error).message}`);
|
||||
}
|
||||
@ -331,7 +422,9 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkFileSystemPermissions(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkFileSystemPermissions(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -346,7 +439,9 @@ export class PreDeploymentChecker {
|
||||
await fs.writeFile(testFile, "test");
|
||||
await fs.unlink(testFile);
|
||||
} catch (error) {
|
||||
errors.push(`Cannot write to logs directory: ${(error as Error).message}`);
|
||||
errors.push(
|
||||
`Cannot write to logs directory: ${(error as Error).message}`
|
||||
);
|
||||
}
|
||||
|
||||
// Check if we can write to backups directory
|
||||
@ -357,11 +452,14 @@ export class PreDeploymentChecker {
|
||||
await fs.writeFile(testFile, "test");
|
||||
await fs.unlink(testFile);
|
||||
} catch (error) {
|
||||
warnings.push(`Cannot write to backups directory: ${(error as Error).message}`);
|
||||
warnings.push(
|
||||
`Cannot write to backups directory: ${(error as Error).message}`
|
||||
);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
errors.push(`File system permission check failed: ${(error as Error).message}`);
|
||||
errors.push(
|
||||
`File system permission check failed: ${(error as Error).message}`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -371,7 +469,9 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkPortAvailability(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkPortAvailability(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -396,9 +496,10 @@ export class PreDeploymentChecker {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
errors.push(`Port availability check failed: ${(error as Error).message}`);
|
||||
errors.push(
|
||||
`Port availability check failed: ${(error as Error).message}`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -408,7 +509,9 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkOpenAIAccess(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkOpenAIAccess(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -423,19 +526,20 @@ export class PreDeploymentChecker {
|
||||
// Test API access (simple models list call)
|
||||
const response = await fetch("https://api.openai.com/v1/models", {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${apiKey}`,
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
errors.push(`OpenAI API access failed: ${response.status} ${response.statusText}`);
|
||||
errors.push(
|
||||
`OpenAI API access failed: ${response.status} ${response.statusText}`
|
||||
);
|
||||
} else {
|
||||
const data = await response.json();
|
||||
if (!data.data || !Array.isArray(data.data)) {
|
||||
warnings.push("OpenAI API returned unexpected response format");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
errors.push(`OpenAI API check failed: ${(error as Error).message}`);
|
||||
}
|
||||
@ -447,7 +551,9 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkTRPCInfrastructure(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkTRPCInfrastructure(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -475,9 +581,10 @@ export class PreDeploymentChecker {
|
||||
} catch (error) {
|
||||
errors.push(`Cannot import tRPC router: ${(error as Error).message}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
errors.push(`tRPC infrastructure check failed: ${(error as Error).message}`);
|
||||
errors.push(
|
||||
`tRPC infrastructure check failed: ${(error as Error).message}`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -487,16 +594,15 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkBatchProcessingReadiness(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkBatchProcessingReadiness(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
try {
|
||||
// Check if batch processing files exist
|
||||
const batchFiles = [
|
||||
"lib/batchProcessor.ts",
|
||||
"lib/batchScheduler.ts",
|
||||
];
|
||||
const batchFiles = ["lib/batchProcessor.ts", "lib/batchScheduler.ts"];
|
||||
|
||||
for (const file of batchFiles) {
|
||||
const fullPath = join(process.cwd(), file);
|
||||
@ -506,29 +612,32 @@ export class PreDeploymentChecker {
|
||||
}
|
||||
|
||||
// Check database readiness for batch processing
|
||||
const batchTableExists = await this.prisma.$queryRaw<{count: string}[]>`
|
||||
const batchTableExists = await this.prisma.$queryRaw<{ count: string }[]>`
|
||||
SELECT COUNT(*) as count
|
||||
FROM information_schema.tables
|
||||
WHERE table_name = 'AIBatchRequest'
|
||||
`;
|
||||
|
||||
if (parseInt(batchTableExists[0]?.count || '0') === 0) {
|
||||
if (parseInt(batchTableExists[0]?.count || "0") === 0) {
|
||||
errors.push("AIBatchRequest table not found");
|
||||
}
|
||||
|
||||
// Check if batch status enum exists
|
||||
const batchStatusExists = await this.prisma.$queryRaw<{count: string}[]>`
|
||||
const batchStatusExists = await this.prisma.$queryRaw<
|
||||
{ count: string }[]
|
||||
>`
|
||||
SELECT COUNT(*) as count
|
||||
FROM pg_type
|
||||
WHERE typname = 'AIBatchRequestStatus'
|
||||
`;
|
||||
|
||||
if (parseInt(batchStatusExists[0]?.count || '0') === 0) {
|
||||
if (parseInt(batchStatusExists[0]?.count || "0") === 0) {
|
||||
errors.push("AIBatchRequestStatus enum not found");
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
errors.push(`Batch processing readiness check failed: ${(error as Error).message}`);
|
||||
errors.push(
|
||||
`Batch processing readiness check failed: ${(error as Error).message}`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -538,7 +647,9 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkSecurityConfiguration(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkSecurityConfiguration(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -556,13 +667,17 @@ export class PreDeploymentChecker {
|
||||
|
||||
// Check if we're running in production mode with proper settings
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
if (!process.env.NEXTAUTH_URL || process.env.NEXTAUTH_URL.includes("localhost")) {
|
||||
if (
|
||||
!process.env.NEXTAUTH_URL ||
|
||||
process.env.NEXTAUTH_URL.includes("localhost")
|
||||
) {
|
||||
warnings.push("NEXTAUTH_URL should not use localhost in production");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
warnings.push(`Security configuration check failed: ${(error as Error).message}`);
|
||||
warnings.push(
|
||||
`Security configuration check failed: ${(error as Error).message}`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -572,31 +687,44 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkPerformanceConfiguration(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkPerformanceConfiguration(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
try {
|
||||
// Check database connection limits
|
||||
const connectionLimit = parseInt(process.env.DATABASE_CONNECTION_LIMIT || "20");
|
||||
const connectionLimit = parseInt(
|
||||
process.env.DATABASE_CONNECTION_LIMIT || "20"
|
||||
);
|
||||
if (connectionLimit < 10) {
|
||||
warnings.push("DATABASE_CONNECTION_LIMIT may be too low for production");
|
||||
warnings.push(
|
||||
"DATABASE_CONNECTION_LIMIT may be too low for production"
|
||||
);
|
||||
}
|
||||
|
||||
// Check batch processing configuration
|
||||
const batchMaxRequests = parseInt(process.env.BATCH_MAX_REQUESTS || "1000");
|
||||
const batchMaxRequests = parseInt(
|
||||
process.env.BATCH_MAX_REQUESTS || "1000"
|
||||
);
|
||||
if (batchMaxRequests > 50000) {
|
||||
warnings.push("BATCH_MAX_REQUESTS exceeds OpenAI limits");
|
||||
}
|
||||
|
||||
// Check session processing concurrency
|
||||
const concurrency = parseInt(process.env.SESSION_PROCESSING_CONCURRENCY || "5");
|
||||
const concurrency = parseInt(
|
||||
process.env.SESSION_PROCESSING_CONCURRENCY || "5"
|
||||
);
|
||||
if (concurrency > 10) {
|
||||
warnings.push("High SESSION_PROCESSING_CONCURRENCY may overwhelm the system");
|
||||
warnings.push(
|
||||
"High SESSION_PROCESSING_CONCURRENCY may overwhelm the system"
|
||||
);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
warnings.push(`Performance configuration check failed: ${(error as Error).message}`);
|
||||
warnings.push(
|
||||
`Performance configuration check failed: ${(error as Error).message}`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -606,7 +734,9 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkBackupValidation(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkBackupValidation(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -625,7 +755,6 @@ export class PreDeploymentChecker {
|
||||
if (!existsSync(backupDir)) {
|
||||
warnings.push("Backup directory does not exist");
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
warnings.push(`Backup validation failed: ${(error as Error).message}`);
|
||||
}
|
||||
@ -637,7 +766,9 @@ export class PreDeploymentChecker {
|
||||
};
|
||||
}
|
||||
|
||||
private async checkRollbackReadiness(): Promise<Omit<CheckResult, 'name' | 'duration'>> {
|
||||
private async checkRollbackReadiness(): Promise<
|
||||
Omit<CheckResult, "name" | "duration">
|
||||
> {
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@ -659,9 +790,10 @@ export class PreDeploymentChecker {
|
||||
if (process.env.MIGRATION_ROLLBACK_ENABLED !== "true") {
|
||||
warnings.push("Rollback is disabled - consider enabling for safety");
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
warnings.push(`Rollback readiness check failed: ${(error as Error).message}`);
|
||||
warnings.push(
|
||||
`Rollback readiness check failed: ${(error as Error).message}`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -676,41 +808,46 @@ export class PreDeploymentChecker {
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
const checker = new PreDeploymentChecker();
|
||||
|
||||
checker.runAllChecks()
|
||||
checker
|
||||
.runAllChecks()
|
||||
.then((result) => {
|
||||
console.log('\n=== PRE-DEPLOYMENT CHECK RESULTS ===');
|
||||
console.log(`Overall Success: ${result.success ? '✅' : '❌'}`);
|
||||
console.log("\n=== PRE-DEPLOYMENT CHECK RESULTS ===");
|
||||
console.log(`Overall Success: ${result.success ? "✅" : "❌"}`);
|
||||
console.log(`Total Duration: ${result.totalDuration}ms`);
|
||||
console.log(`Critical Failures: ${result.criticalFailures}`);
|
||||
console.log(`Total Warnings: ${result.warningCount}`);
|
||||
|
||||
console.log('\n=== INDIVIDUAL CHECKS ===');
|
||||
console.log("\n=== INDIVIDUAL CHECKS ===");
|
||||
for (const check of result.checks) {
|
||||
const status = check.success ? '✅' : '❌';
|
||||
const critical = check.critical ? ' (CRITICAL)' : '';
|
||||
const status = check.success ? "✅" : "❌";
|
||||
const critical = check.critical ? " (CRITICAL)" : "";
|
||||
console.log(`${status} ${check.name}${critical} (${check.duration}ms)`);
|
||||
|
||||
if (check.errors.length > 0) {
|
||||
check.errors.forEach(error => console.log(` ❌ ${error}`));
|
||||
check.errors.forEach((error) => console.log(` ❌ ${error}`));
|
||||
}
|
||||
|
||||
if (check.warnings.length > 0) {
|
||||
check.warnings.forEach(warning => console.log(` ⚠️ ${warning}`));
|
||||
check.warnings.forEach((warning) => console.log(` ⚠️ ${warning}`));
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
console.log('\n❌ DEPLOYMENT BLOCKED - Fix critical issues before proceeding');
|
||||
console.log(
|
||||
"\n❌ DEPLOYMENT BLOCKED - Fix critical issues before proceeding"
|
||||
);
|
||||
} else if (result.warningCount > 0) {
|
||||
console.log('\n⚠️ DEPLOYMENT ALLOWED - Review warnings before proceeding');
|
||||
console.log(
|
||||
"\n⚠️ DEPLOYMENT ALLOWED - Review warnings before proceeding"
|
||||
);
|
||||
} else {
|
||||
console.log('\n✅ DEPLOYMENT READY - All checks passed');
|
||||
console.log("\n✅ DEPLOYMENT READY - All checks passed");
|
||||
}
|
||||
|
||||
process.exit(result.success ? 0 : 1);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Pre-deployment checks failed:', error);
|
||||
console.error("Pre-deployment checks failed:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user