production-current #1

Merged
admin merged 32 commits from production-current into master 2025-11-09 21:09:23 -05:00
2 changed files with 141 additions and 3 deletions
Showing only changes of commit c709e8b8c4 - Show all commits

View File

@@ -17,4 +17,23 @@ fs.writeFileSync(path.join(distDir, 'package.json'), JSON.stringify(newPackage,
fs.copyFileSync(path.join(projectRoot, 'package-lock.json'), path.join(distDir, 'package-lock.json'));
// Copy assets directory if it exists
const assetsSrcDir = path.join(projectRoot, 'src', 'assets');
const assetsDistDir = path.join(distDir, 'assets');
if (fs.existsSync(assetsSrcDir)) {
if (!fs.existsSync(assetsDistDir)) {
fs.mkdirSync(assetsDistDir, { recursive: true });
}
// Copy all files from assets directory
const files = fs.readdirSync(assetsSrcDir);
files.forEach(file => {
const srcPath = path.join(assetsSrcDir, file);
const distPath = path.join(assetsDistDir, file);
fs.copyFileSync(srcPath, distPath);
console.log(`Copied ${file} to dist/assets/`);
});
}
console.log('Production package.json and package-lock.json created in dist/');

View File

@@ -70,7 +70,7 @@ class PDFGenerationService {
*/
private async getBrowser(): Promise<any> {
if (!this.browser) {
this.browser = await puppeteer.launch({
const launchOptions: any = {
headless: 'new',
args: [
'--no-sandbox',
@@ -84,7 +84,14 @@ class PDFGenerationService {
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding',
],
});
};
// For Firebase Functions environment, use the bundled Chrome
if (process.env.FUNCTIONS_EMULATOR || process.env.FIREBASE_FUNCTIONS) {
launchOptions.executablePath = process.env.PUPPETEER_EXECUTABLE_PATH || '/usr/bin/google-chrome-stable';
}
this.browser = await puppeteer.launch(launchOptions);
}
return this.browser;
}
@@ -741,11 +748,115 @@ class PDFGenerationService {
return pdfBuffer;
} catch (error) {
logger.error('Failed to generate CIM Review PDF', error);
logger.error('Failed to generate CIM Review PDF with Puppeteer, trying fallback method', error);
// Fallback: Generate a simple text-based PDF without Chrome
return this.generateSimplePDF(analysisData);
}
}
/**
* Generate a simple PDF without Chrome (fallback method)
*/
private async generateSimplePDF(analysisData: any): Promise<Buffer> {
try {
// Create a simple HTML document that can be converted to PDF
const simpleHTML = this.generateSimpleHTML(analysisData);
// For now, return a simple text representation
// In a production environment, you might want to use a different PDF library
const textContent = this.convertAnalysisToText(analysisData);
// Create a simple HTML document
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CIM Review Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
h1 { color: #333; border-bottom: 2px solid #007bff; }
h2 { color: #555; margin-top: 30px; }
.section { margin-bottom: 20px; }
.field { margin-bottom: 10px; }
.label { font-weight: bold; color: #666; }
.value { margin-left: 10px; }
</style>
</head>
<body>
<h1>BLUEPOINT Capital Partners</h1>
<h2>CIM Review Report</h2>
<p><strong>Generated:</strong> ${new Date().toLocaleDateString()} at ${new Date().toLocaleTimeString()}</p>
<hr>
${textContent}
<hr>
<p><em>BLUEPOINT Capital Partners | CIM Document Processor | Confidential</em></p>
</body>
</html>
`;
// Try to generate PDF with Puppeteer again, but with different options
try {
const page = await this.getPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
const buffer = await page.pdf({
format: 'A4',
margin: { top: '0.5in', right: '0.5in', bottom: '0.5in', left: '0.5in' },
printBackground: true,
});
this.releasePage(page);
return buffer;
} catch (puppeteerError) {
logger.error('Puppeteer fallback also failed, returning error response', puppeteerError);
throw new Error('PDF generation is currently unavailable. Please try again later.');
}
} catch (error) {
logger.error('Simple PDF generation failed', error);
throw error;
}
}
/**
* Convert analysis data to simple text format
*/
private convertAnalysisToText(analysisData: any): string {
let text = '';
const sections = [
{ title: 'Deal Overview', data: analysisData.dealOverview },
{ title: 'Business Description', data: analysisData.businessDescription },
{ title: 'Market & Industry Analysis', data: analysisData.marketIndustryAnalysis },
{ title: 'Financial Summary', data: analysisData.financialSummary },
{ title: 'Management Team Overview', data: analysisData.managementTeamOverview },
{ title: 'Preliminary Investment Thesis', data: analysisData.preliminaryInvestmentThesis },
{ title: 'Key Questions & Next Steps', data: analysisData.keyQuestionsNextSteps },
];
sections.forEach(section => {
if (section.data) {
text += `<div class="section"><h2>${section.title}</h2>`;
Object.entries(section.data).forEach(([key, value]) => {
if (value && typeof value !== 'object') {
text += `<div class="field"><span class="label">${this.formatFieldName(key)}:</span><span class="value">${value}</span></div>`;
}
});
text += '</div>';
}
});
return text;
}
/**
* Generate simple HTML for fallback PDF generation
*/
private generateSimpleHTML(analysisData: any): string {
return this.convertAnalysisToText(analysisData);
}
/**
* Generate HTML from CIM Review analysis data
*/
@@ -1074,6 +1185,7 @@ class PDFGenerationService {
<div class="container">
<div class="header">
<div class="header-left">
${this.getLogoBase64() ? `
<div class="logo-container">
<img src="data:image/png;base64,${this.getLogoBase64()}" alt="Bluepoint Capital Partners" class="logo" />
<div class="company-info">
@@ -1085,6 +1197,12 @@ class PDFGenerationService {
<h1 class="title">CIM Review Report</h1>
<p class="subtitle">Comprehensive Investment Memorandum Analysis</p>
</div>
` : `
<div>
<h1 class="title">CIM Review Report</h1>
<p class="subtitle">BLUEPOINT Capital Partners - Professional Investment Analysis</p>
</div>
`}
</div>
<div class="meta">
<div>Generated on ${new Date().toLocaleDateString()}</div>
@@ -1189,6 +1307,7 @@ class PDFGenerationService {
return logoBuffer.toString('base64');
} catch (error) {
logger.error('Failed to load logo:', error);
// Return empty string if logo not found - this will hide the logo but allow PDF generation to continue
return '';
}
}