CatPtain commited on
Commit
418f3d8
·
verified ·
1 Parent(s): 53be694

Upload public.js

Browse files
Files changed (1) hide show
  1. backend/src/routes/public.js +117 -53
backend/src/routes/public.js CHANGED
@@ -357,7 +357,8 @@ router.post('/generate-share-link', async (req, res, next) => {
357
  }
358
 
359
  const baseUrl = process.env.PUBLIC_URL || req.get('host');
360
- const protocol = process.env.NODE_ENV === 'production' ? 'https' : req.protocol;
 
361
 
362
  const shareLinks = {
363
  // Single page share link
@@ -475,20 +476,13 @@ router.get('/screenshot/:userId/:pptId/:slideIndex?', async (req, res, next) =>
475
  }
476
  });
477
 
478
- // 🔥 关键修改:image端点现在直接返回SVG图片,不再返回截图工具页面
479
  router.get('/image/:userId/:pptId/:slideIndex?', async (req, res, next) => {
480
- // 🚨 强制直接返回SVG图片 - 修复Hugging Face部署问题
481
  try {
482
  const { userId, pptId, slideIndex = 0 } = req.params;
 
483
 
484
- console.log(`🔥 FORCE SVG GENERATION: userId=${userId}, pptId=${pptId}, slideIndex=${slideIndex}`);
485
-
486
- // 立即设置SVG响应头,确保浏览器知道这是图片
487
- res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
488
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
489
- res.setHeader('Access-Control-Allow-Origin', '*');
490
- res.setHeader('X-Force-Update', 'true');
491
- res.setHeader('X-Timestamp', new Date().toISOString());
492
 
493
  // Get PPT data
494
  const fileName = `${pptId}.json`;
@@ -509,61 +503,131 @@ router.get('/image/:userId/:pptId/:slideIndex?', async (req, res, next) => {
509
 
510
  if (!pptData) {
511
  console.log(`❌ PPT not found: ${pptId}`);
512
- const notFoundSvg = `<svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
513
- <rect width="100%" height="100%" fill="#f8f9fa"/>
514
- <text x="400" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" fill="#6c757d">PPT Not Found</text>
515
- <text x="400" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" fill="#6c757d">PPT ${pptId} does not exist</text>
516
- <text x="400" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#999">${new Date().toISOString()}</text>
517
- </svg>`;
518
 
519
- return res.send(notFoundSvg);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  }
521
 
522
  const slideIdx = parseInt(slideIndex);
523
  if (slideIdx >= pptData.slides.length || slideIdx < 0) {
524
  console.log(`❌ Invalid slide index: ${slideIndex}`);
525
- const invalidSlideSvg = `<svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
526
- <rect width="100%" height="100%" fill="#f8f9fa"/>
527
- <text x="400" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" fill="#6c757d">Invalid Slide</text>
528
- <text x="400" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" fill="#6c757d">Slide ${slideIndex} not found</text>
529
- <text x="400" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#999">${new Date().toISOString()}</text>
530
- </svg>`;
531
 
532
- return res.send(invalidSlideSvg);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
533
  }
534
 
535
- console.log(`✅ Generating SVG for slide ${slideIdx}`);
536
-
537
- // 生成PPT幻灯片的SVG图片
538
- const slide = pptData.slides[slideIdx];
539
- const defaultWidth = pptData.viewportSize || 1000;
540
- const defaultHeight = Math.ceil(defaultWidth * (pptData.viewportRatio || 0.5625));
541
-
542
- const svgContent = generateSlideSVG(slide, pptData, {
543
- width: defaultWidth,
544
- height: defaultHeight
545
- });
546
-
547
- console.log(`🎉 SVG generated successfully, returning image data`);
548
-
549
- // 直接返回SVG图片内容
550
- res.send(svgContent);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
 
552
  } catch (error) {
553
  console.error('❌ Image generation failed:', error);
554
 
555
- // 确保仍然返回SVG格式的错误信息
556
- res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
557
- res.setHeader('Access-Control-Allow-Origin', '*');
558
-
559
- const errorSvg = `<svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
560
- <rect width="100%" height="100%" fill="#ffe6e6"/>
561
- <text x="400" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" fill="#cc0000">Generation Error</text>
562
- <text x="400" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" fill="#cc0000">Failed to generate image</text>
563
- <text x="400" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#999">${new Date().toISOString()}</text>
564
- </svg>`;
565
-
566
- res.send(errorSvg);
 
 
 
 
 
 
567
  }
568
  });
569
 
 
357
  }
358
 
359
  const baseUrl = process.env.PUBLIC_URL || req.get('host');
360
+ // 修复协议判断:localhost 始终使用 http,生产环境使用 https
361
+ const protocol = baseUrl.includes('localhost') ? 'http' : 'https';
362
 
363
  const shareLinks = {
364
  // Single page share link
 
476
  }
477
  });
478
 
479
+ // 🔥 关键修改:image端点现在支持多种格式,包括SVG和JPG
480
  router.get('/image/:userId/:pptId/:slideIndex?', async (req, res, next) => {
 
481
  try {
482
  const { userId, pptId, slideIndex = 0 } = req.params;
483
+ const { format = 'svg', quality = 90, width: requestWidth, height: requestHeight } = req.query;
484
 
485
+ console.log(`🔥 Image request: userId=${userId}, pptId=${pptId}, slideIndex=${slideIndex}, format=${format}`);
 
 
 
 
 
 
 
486
 
487
  // Get PPT data
488
  const fileName = `${pptId}.json`;
 
503
 
504
  if (!pptData) {
505
  console.log(`❌ PPT not found: ${pptId}`);
 
 
 
 
 
 
506
 
507
+ if (format === 'svg') {
508
+ const notFoundSvg = `<svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
509
+ <rect width="100%" height="100%" fill="#f8f9fa"/>
510
+ <text x="400" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" fill="#6c757d">PPT Not Found</text>
511
+ <text x="400" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" fill="#6c757d">PPT ${pptId} does not exist</text>
512
+ <text x="400" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#999">${new Date().toISOString()}</text>
513
+ </svg>`;
514
+
515
+ res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
516
+ return res.send(notFoundSvg);
517
+ } else {
518
+ // 对于JPG/PNG格式,返回截图工具页面
519
+ const errorPage = generateErrorPage('PPT Not Found', `PPT ${pptId} not found. Please check the PPT ID.`);
520
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
521
+ return res.status(404).send(errorPage);
522
+ }
523
  }
524
 
525
  const slideIdx = parseInt(slideIndex);
526
  if (slideIdx >= pptData.slides.length || slideIdx < 0) {
527
  console.log(`❌ Invalid slide index: ${slideIndex}`);
 
 
 
 
 
 
528
 
529
+ if (format === 'svg') {
530
+ const invalidSlideSvg = `<svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
531
+ <rect width="100%" height="100%" fill="#f8f9fa"/>
532
+ <text x="400" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" fill="#6c757d">Invalid Slide</text>
533
+ <text x="400" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" fill="#6c757d">Slide ${slideIndex} not found</text>
534
+ <text x="400" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#999">${new Date().toISOString()}</text>
535
+ </svg>`;
536
+
537
+ res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
538
+ return res.send(invalidSlideSvg);
539
+ } else {
540
+ const errorPage = generateErrorPage('Invalid Slide', `Slide ${slideIndex} not found in this PPT.`);
541
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
542
+ return res.status(404).send(errorPage);
543
+ }
544
  }
545
 
546
+ // 根据格式处理
547
+ if (format === 'svg') {
548
+ // SVG格式 - 直接生成并返回
549
+ console.log(`✅ Generating SVG for slide ${slideIdx}`);
550
+
551
+ const defaultWidth = pptData.viewportSize || 1000;
552
+ const defaultHeight = Math.ceil(defaultWidth * (pptData.viewportRatio || 0.5625));
553
+ const finalWidth = requestWidth ? parseInt(requestWidth) : defaultWidth;
554
+ const finalHeight = requestHeight ? parseInt(requestHeight) : defaultHeight;
555
+
556
+ const slide = pptData.slides[slideIdx];
557
+ const svgContent = generateSlideSVG(slide, pptData, {
558
+ width: finalWidth,
559
+ height: finalHeight
560
+ });
561
+
562
+ console.log(`🎉 SVG generated successfully, returning image data`);
563
+
564
+ res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
565
+ res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
566
+ res.setHeader('Access-Control-Allow-Origin', '*');
567
+ res.setHeader('X-Force-Update', 'true');
568
+ res.setHeader('X-Generation-Time', '< 10ms');
569
+ res.send(svgContent);
570
+
571
+ } else {
572
+ // JPG/PNG格式 - 直接生成并返回图片
573
+ console.log(`✅ Generating ${format} image directly`);
574
+
575
+ try {
576
+ // 使用共享模块生成HTML用于后端截图
577
+ const htmlContent = generateSlideHTML(pptData, slideIdx, { format: 'screenshot' });
578
+
579
+ const screenshot = await screenshotService.generateScreenshot(htmlContent, {
580
+ format: format === 'jpg' ? 'jpeg' : format,
581
+ quality: parseInt(quality),
582
+ width: pptData.viewportSize || 1000,
583
+ height: Math.ceil((pptData.viewportSize || 1000) * (pptData.viewportRatio || 0.5625))
584
+ });
585
+
586
+ res.setHeader('Content-Type', `image/${format === 'jpg' ? 'jpeg' : format}`);
587
+ res.setHeader('X-Screenshot-Type', 'backend-generated');
588
+ res.setHeader('X-Generation-Time', '2-5s');
589
+ res.setHeader('Cache-Control', 'public, max-age=3600'); // 缓存1小时
590
+ res.send(screenshot);
591
+
592
+ } catch (error) {
593
+ console.error(`Backend ${format} generation failed:`, error);
594
+
595
+ // 生成后备图片
596
+ const fallbackImage = screenshotService.generateFallbackImage(
597
+ pptData.viewportSize || 1000,
598
+ Math.ceil((pptData.viewportSize || 1000) * (pptData.viewportRatio || 0.5625)),
599
+ `PPT ${format.toUpperCase()} Image`,
600
+ 'Image generation failed'
601
+ );
602
+
603
+ res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
604
+ res.setHeader('X-Screenshot-Type', 'fallback-generated');
605
+ res.setHeader('X-Generation-Time', '< 50ms');
606
+ res.send(fallbackImage);
607
+ }
608
+ }
609
 
610
  } catch (error) {
611
  console.error('❌ Image generation failed:', error);
612
 
613
+ // 根据格式返回错误
614
+ if (req.query.format === 'svg') {
615
+ res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
616
+ res.setHeader('Access-Control-Allow-Origin', '*');
617
+
618
+ const errorSvg = `<svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
619
+ <rect width="100%" height="100%" fill="#ffe6e6"/>
620
+ <text x="400" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" fill="#cc0000">Generation Error</text>
621
+ <text x="400" y="250" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" fill="#cc0000">Failed to generate image</text>
622
+ <text x="400" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#999">${new Date().toISOString()}</text>
623
+ </svg>`;
624
+
625
+ res.send(errorSvg);
626
+ } else {
627
+ const errorPage = generateErrorPage('Generation Error', 'Failed to generate the requested image format.');
628
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
629
+ res.status(500).send(errorPage);
630
+ }
631
  }
632
  });
633