Spaces:
Running
Running
import express from 'express'; | |
import { v4 as uuidv4 } from 'uuid'; | |
import githubService from '../services/githubService.js'; | |
const router = express.Router(); | |
// 添加路由级别的日志中间件 | |
router.use((req, res, next) => { | |
console.log(`PPT Router - ${req.method} ${req.path} - Body:`, Object.keys(req.body || {})); | |
next(); | |
}); | |
// 获取用户的PPT列表 - 优先从Huggingface硬盘读取 | |
router.get('/list', async (req, res, next) => { | |
try { | |
const userId = req.user.userId; | |
// 优先从Huggingface存储服务获取PPT列表 | |
const { default: huggingfaceStorageService } = await import('../services/huggingfaceStorageService.js'); | |
let pptList = []; | |
try { | |
// 尝试从Huggingface硬盘读取PPT列表 | |
pptList = await huggingfaceStorageService.getUserPPTList(userId); | |
console.log(`📁 Found ${pptList.length} PPTs from Huggingface storage for user ${userId}`); | |
} catch (hfError) { | |
console.warn('⚠️ Huggingface storage unavailable, falling back to GitHub:', hfError.message); | |
// 回退到GitHub存储 | |
pptList = await githubService.getUserPPTList(userId); | |
} | |
res.json(pptList); | |
} catch (error) { | |
next(error); | |
} | |
}); | |
// 获取指定PPT数据 - 优先从Huggingface硬盘读取 | |
router.get('/:pptId', async (req, res, next) => { | |
try { | |
const userId = req.user.userId; | |
const { pptId } = req.params; | |
console.log(`🔍 Fetching PPT: ${pptId} for user: ${userId}`); | |
let pptData = null; | |
let foundInRepo = -1; | |
// 优先从Huggingface存储服务获取PPT数据 | |
const { default: huggingfaceStorageService } = await import('../services/huggingfaceStorageService.js'); | |
try { | |
// 尝试从Huggingface硬盘读取PPT数据 | |
pptData = await huggingfaceStorageService.getPPTData(userId, pptId); | |
foundInRepo = 0; | |
console.log(`✅ PPT found in Huggingface storage`); | |
} catch (hfError) { | |
console.warn('⚠️ PPT not found in Huggingface storage, trying other sources:', hfError.message); | |
if (githubService.useMemoryStorage) { | |
// 内存存储模式 | |
const result = await githubService.getPPTFromMemory(userId, pptId); | |
if (result && result.content) { | |
pptData = result.content; | |
foundInRepo = 0; | |
console.log(`✅ PPT found in memory storage`); | |
} | |
} else { | |
// GitHub存储模式 - 尝试所有仓库 | |
console.log(`Available repositories: ${githubService.repositories.length}`); | |
for (let i = 0; i < githubService.repositories.length; i++) { | |
try { | |
console.log(`📂 Checking repository ${i}: ${githubService.repositories[i]}`); | |
const result = await githubService.getPPT(userId, pptId, i); | |
if (result && result.content) { | |
pptData = result.content; | |
foundInRepo = i; | |
console.log(`✅ PPT found in repository ${i}${result.isReassembled ? ' (reassembled from chunks)' : ''}`); | |
break; | |
} | |
} catch (error) { | |
console.log(`❌ PPT not found in repository ${i}: ${error.message}`); | |
continue; | |
} | |
} | |
} | |
} | |
if (!pptData) { | |
console.log(`❌ PPT ${pptId} not found for user ${userId}`); | |
return res.status(404).json({ | |
error: 'PPT not found', | |
pptId: pptId, | |
userId: userId, | |
storageMode: githubService.useMemoryStorage ? 'memory' : 'github' | |
}); | |
} | |
// 🔧 修复:标准化PPT数据格式,确保前端兼容性 | |
const standardizedPptData = { | |
// 确保基本字段存在 | |
id: pptData.id || pptData.pptId || pptId, | |
pptId: pptData.pptId || pptData.id || pptId, | |
title: pptData.title || '未命名演示文稿', | |
// 标准化slides数组 | |
slides: Array.isArray(pptData.slides) ? pptData.slides.map((slide, index) => ({ | |
id: slide.id || `slide-${index}`, | |
elements: Array.isArray(slide.elements) ? slide.elements : [], | |
background: slide.background || { type: 'solid', color: '#ffffff' }, | |
...slide | |
})) : [], | |
// 标准化主题 | |
theme: pptData.theme || { | |
backgroundColor: '#ffffff', | |
themeColor: '#d14424', | |
fontColor: '#333333', | |
fontName: 'Microsoft YaHei' | |
}, | |
// 🔧 关键修复:确保视口信息正确传递 | |
viewportSize: pptData.viewportSize || 1000, | |
viewportRatio: pptData.viewportRatio || 0.5625, | |
// 时间戳 | |
createdAt: pptData.createdAt || new Date().toISOString(), | |
updatedAt: pptData.updatedAt || new Date().toISOString(), | |
// 保留其他可能的属性 | |
...pptData | |
}; | |
// 🔧 新增:数据验证和修复 | |
if (standardizedPptData.slides.length === 0) { | |
console.log(`⚠️ PPT ${pptId} has no slides, creating default slide`); | |
standardizedPptData.slides = [{ | |
id: 'default-slide', | |
elements: [], | |
background: { type: 'solid', color: '#ffffff' } | |
}]; | |
} | |
// 验证视口比例的合理性 | |
if (standardizedPptData.viewportRatio <= 0 || standardizedPptData.viewportRatio > 2) { | |
console.log(`⚠️ Invalid viewportRatio ${standardizedPptData.viewportRatio}, resetting to 0.5625`); | |
standardizedPptData.viewportRatio = 0.5625; | |
} | |
// 验证视口尺寸的合理性 | |
if (standardizedPptData.viewportSize <= 0 || standardizedPptData.viewportSize > 2000) { | |
console.log(`⚠️ Invalid viewportSize ${standardizedPptData.viewportSize}, resetting to 1000`); | |
standardizedPptData.viewportSize = 1000; | |
} | |
console.log(`✅ Successfully found and standardized PPT ${pptId}:`, { | |
slidesCount: standardizedPptData.slides.length, | |
viewportSize: standardizedPptData.viewportSize, | |
viewportRatio: standardizedPptData.viewportRatio, | |
storageMode: githubService.useMemoryStorage ? 'memory' : `repository ${foundInRepo}` | |
}); | |
res.json(standardizedPptData); | |
} catch (error) { | |
console.error(`❌ Error fetching PPT ${req.params.pptId}:`, error); | |
next(error); | |
} | |
}); | |
// 保存PPT数据 - 新架构版本 | |
router.post('/save', async (req, res, next) => { | |
try { | |
const userId = req.user.userId; | |
const { pptId, title, slides, theme, viewportSize, viewportRatio } = req.body; | |
console.log(`🔄 Starting PPT save for user ${userId}, PPT ID: ${pptId}`); | |
console.log(`📊 Request body analysis:`, { | |
pptId: !!pptId, | |
title: title?.length || 0, | |
slidesCount: Array.isArray(slides) ? slides.length : 'not array', | |
theme: !!theme, | |
requestSize: req.get('content-length'), | |
storageMode: githubService.useMemoryStorage ? 'memory' : 'github' | |
}); | |
if (!pptId || !slides) { | |
console.log(`❌ Missing required fields - pptId: ${!!pptId}, slides: ${!!slides}`); | |
return res.status(400).json({ error: 'PPT ID and slides are required' }); | |
} | |
// 验证slides数组 | |
if (!Array.isArray(slides) || slides.length === 0) { | |
console.log(`❌ Invalid slides array - isArray: ${Array.isArray(slides)}, length: ${slides?.length || 0}`); | |
return res.status(400).json({ error: 'Slides must be a non-empty array' }); | |
} | |
// 构建标准化的PPT数据结构 | |
const pptData = { | |
id: pptId, | |
title: title || '未命名演示文稿', | |
slides: slides.map((slide, index) => ({ | |
id: slide.id || `slide-${index}`, | |
elements: Array.isArray(slide.elements) ? slide.elements : [], | |
background: slide.background || { type: 'solid', color: '#ffffff' }, | |
...slide | |
})), | |
theme: theme || { | |
backgroundColor: '#ffffff', | |
themeColor: '#d14424', | |
fontColor: '#333333', | |
fontName: 'Microsoft YaHei' | |
}, | |
viewportSize: viewportSize || 1000, | |
viewportRatio: viewportRatio || 0.5625, | |
createdAt: new Date().toISOString(), | |
updatedAt: new Date().toISOString() | |
}; | |
// 计算文件大小分析 | |
const jsonData = JSON.stringify(pptData); | |
const totalDataSize = Buffer.byteLength(jsonData, 'utf8'); | |
console.log(`📊 PPT data analysis:`); | |
console.log(` - Total slides: ${slides.length}`); | |
console.log(` - Total data size: ${totalDataSize} bytes (${(totalDataSize / 1024).toFixed(2)} KB)`); | |
console.log(` - Average slide size: ${(totalDataSize / slides.length / 1024).toFixed(2)} KB`); | |
// 分析每个slide的大小 | |
const slideAnalysis = slides.map((slide, index) => { | |
const slideJson = JSON.stringify(slide); | |
const slideSize = Buffer.byteLength(slideJson, 'utf8'); | |
return { | |
index, | |
size: slideSize, | |
sizeKB: (slideSize / 1024).toFixed(2), | |
elementsCount: slide.elements?.length || 0, | |
hasLargeContent: slideSize > 800 * 1024 // 800KB+ | |
}; | |
}); | |
const largeSlides = slideAnalysis.filter(s => s.hasLargeContent); | |
const maxSlideSize = Math.max(...slideAnalysis.map(s => s.size)); | |
console.log(`📋 Slide size analysis:`); | |
console.log(` - Largest slide: ${(maxSlideSize / 1024).toFixed(2)} KB`); | |
console.log(` - Large slides (>800KB): ${largeSlides.length}`); | |
if (largeSlides.length > 0) { | |
console.log(` - Large slide indices: ${largeSlides.map(s => s.index).join(', ')}`); | |
} | |
try { | |
let result; | |
// 优先保存到Huggingface存储服务 | |
const { default: huggingfaceStorageService } = await import('../services/huggingfaceStorageService.js'); | |
try { | |
// 尝试保存到Huggingface硬盘 | |
result = await huggingfaceStorageService.storePPTData(userId, pptId, pptData); | |
console.log(`✅ PPT saved to Huggingface storage:`, result); | |
} catch (hfError) { | |
console.warn('⚠️ Failed to save to Huggingface storage, falling back to other storage:', hfError.message); | |
console.log(`💾 Using fallback storage mode: ${githubService.useMemoryStorage ? 'memory' : 'GitHub folder architecture'}`); | |
if (githubService.useMemoryStorage) { | |
// 内存存储模式 | |
result = await githubService.savePPTToMemory(userId, pptId, pptData); | |
console.log(`✅ PPT saved to memory storage:`, result); | |
} else { | |
// GitHub存储模式 - 使用新的文件夹架构 | |
console.log(`🐙 Using GitHub folder storage for ${pptId}`); | |
console.log(`📂 Available repositories: ${githubService.repositories?.length || 0}`); | |
if (!githubService.repositories || githubService.repositories.length === 0) { | |
throw new Error('No GitHub repositories configured'); | |
} | |
console.log(`🔍 Pre-save validation:`); | |
console.log(` - Repository 0: ${githubService.repositories[0]}`); | |
console.log(` - Token configured: ${!!githubService.token}`); | |
console.log(` - API URL: ${githubService.apiUrl}`); | |
result = await githubService.savePPT(userId, pptId, pptData, 0); | |
console.log(`✅ PPT saved to GitHub folder storage:`, result); | |
} | |
} | |
console.log(`✅ PPT save completed successfully:`, { | |
pptId, | |
userId, | |
slideCount: slides.length, | |
totalDataSize, | |
storageType: result.storage || 'unknown', | |
compressionApplied: result.compressionSummary?.compressedSlides > 0, | |
storageMode: githubService.useMemoryStorage ? 'memory' : 'github' | |
}); | |
const response = { | |
message: 'PPT saved successfully', | |
pptId, | |
slidesCount: slides.length, | |
savedAt: new Date().toISOString(), | |
totalFileSize: `${(totalDataSize / 1024).toFixed(2)} KB`, | |
storageType: result.storage || 'unknown', | |
storageMode: githubService.useMemoryStorage ? 'memory' : 'github', | |
architecture: 'folder-based', | |
slideAnalysis: { | |
totalSlides: slides.length, | |
largestSlideSize: `${(maxSlideSize / 1024).toFixed(2)} KB`, | |
largeSlides: largeSlides.length, | |
averageSlideSize: `${(totalDataSize / slides.length / 1024).toFixed(2)} KB` | |
} | |
}; | |
// 添加压缩信息 | |
if (result.compressionSummary) { | |
response.compression = { | |
applied: result.compressionSummary.compressedSlides > 0, | |
compressedSlides: result.compressionSummary.compressedSlides, | |
totalSlides: result.compressionSummary.totalSlides, | |
savedSlides: result.compressionSummary.savedSlides, | |
failedSlides: result.compressionSummary.failedSlides, | |
originalSize: `${(result.compressionSummary.totalOriginalSize / 1024).toFixed(2)} KB`, | |
finalSize: `${(result.compressionSummary.totalFinalSize / 1024).toFixed(2)} KB`, | |
savedSpace: `${((result.compressionSummary.totalOriginalSize - result.compressionSummary.totalFinalSize) / 1024).toFixed(2)} KB`, | |
compressionRatio: `${(result.compressionSummary.compressionRatio * 100).toFixed(1)}%` | |
}; | |
if (result.compressionSummary.compressedSlides > 0) { | |
response.message = `PPT saved successfully with ${result.compressionSummary.compressedSlides} slides optimized for storage`; | |
response.optimization = `Automatic compression applied to ${result.compressionSummary.compressedSlides} large slides, saving ${response.compression.savedSpace}`; | |
} | |
// 添加保存警告信息 | |
if (result.compressionSummary.failedSlides > 0) { | |
response.warning = `${result.compressionSummary.failedSlides} slides failed to save`; | |
response.partialSave = true; | |
response.savedSlides = result.compressionSummary.savedSlides; | |
response.failedSlides = result.compressionSummary.failedSlides; | |
if (result.compressionSummary.errors) { | |
response.slideErrors = result.compressionSummary.errors; | |
} | |
} | |
} | |
// 添加存储路径信息 | |
if (result.folderPath) { | |
response.storagePath = result.folderPath; | |
response.architecture = 'folder-based (meta.json + individual slide files)'; | |
} | |
// 添加警告信息 | |
if (result.warnings) { | |
response.warnings = result.warnings; | |
} | |
// 记录PPT修改,用于自动备份 | |
try { | |
const { default: autoBackupService } = await import('../services/autoBackupService.js'); | |
autoBackupService.recordPPTModification(userId, pptId); | |
} catch (backupError) { | |
console.warn('⚠️ Failed to record PPT modification for auto backup:', backupError.message); | |
} | |
// 🔄 新增:自动更新持久化图片链接 | |
try { | |
const { default: persistentImageLinkService } = await import('../services/persistentImageLinkService.js'); | |
// 异步更新所有页面的持久化链接(不阻塞响应) | |
setImmediate(async () => { | |
try { | |
console.log(`🔄 Starting persistent image links update for PPT ${pptId}`); | |
const updateResults = await persistentImageLinkService.updateAllPersistentLinksWithFrontendExport( | |
userId, | |
pptId, | |
slides, | |
{ | |
format: 'jpg', | |
quality: 0.9, | |
viewportSize: viewportSize || 1000, | |
viewportRatio: viewportRatio || 0.5625 | |
} | |
); | |
const successCount = updateResults.filter(r => r.success).length; | |
const failCount = updateResults.filter(r => !r.success).length; | |
console.log(`✅ Persistent image links update completed: ${successCount} success, ${failCount} failed`); | |
if (failCount > 0) { | |
console.warn(`⚠️ Some persistent image links failed to update:`, | |
updateResults.filter(r => !r.success).map(r => `Page ${r.pageIndex}: ${r.error}`) | |
); | |
} | |
} catch (updateError) { | |
console.error('❌ Failed to update persistent image links:', updateError.message); | |
} | |
}); | |
// 添加持久化链接信息到响应 | |
response.persistentLinks = { | |
enabled: true, | |
updateTriggered: true, | |
message: 'Persistent image links update started in background' | |
}; | |
} catch (persistentError) { | |
console.warn('⚠️ Failed to trigger persistent image links update:', persistentError.message); | |
response.persistentLinks = { | |
enabled: false, | |
error: persistentError.message | |
}; | |
} | |
res.json(response); | |
} catch (saveError) { | |
console.error(`❌ Save operation failed:`, { | |
error: saveError.message, | |
stack: saveError.stack, | |
userId, | |
pptId, | |
slidesCount: slides.length, | |
totalDataSize, | |
errorName: saveError.name, | |
errorCode: saveError.code | |
}); | |
let errorResponse = { | |
error: 'PPT save failed', | |
details: saveError.message, | |
pptId: pptId, | |
slidesCount: slides.length, | |
totalFileSize: `${(totalDataSize / 1024).toFixed(2)} KB`, | |
timestamp: new Date().toISOString(), | |
slideAnalysis: { | |
largestSlideSize: `${(maxSlideSize / 1024).toFixed(2)} KB`, | |
largeSlides: largeSlides.length | |
} | |
}; | |
// 根据错误类型提供具体建议 | |
if (saveError.message.includes('File too large') || saveError.message.includes('exceeds')) { | |
errorResponse.error = 'Slide file too large for storage'; | |
errorResponse.suggestion = 'Some slides are too large even after compression. Try reducing image sizes or slide complexity.'; | |
errorResponse.limits = { | |
maxSlideSize: '999 KB per slide', | |
largestSlideSize: `${(maxSlideSize / 1024).toFixed(2)} KB`, | |
oversizeSlides: largeSlides.length | |
}; | |
return res.status(413).json(errorResponse); | |
} | |
if (saveError.message.includes('Failed to compress slide')) { | |
errorResponse.error = 'Slide compression failed'; | |
errorResponse.suggestion = 'Unable to compress slides to acceptable size. Try manually reducing content complexity.'; | |
errorResponse.compressionError = true; | |
return res.status(500).json(errorResponse); | |
} | |
if (saveError.message.includes('GitHub API') || saveError.message.includes('GitHub')) { | |
errorResponse.error = 'GitHub storage service error'; | |
errorResponse.suggestion = 'Temporary GitHub service issue. Please try again in a few minutes.'; | |
errorResponse.githubError = true; | |
return res.status(502).json(errorResponse); | |
} | |
if (saveError.message.includes('No GitHub repositories')) { | |
errorResponse.error = 'Storage configuration error'; | |
errorResponse.suggestion = 'GitHub repositories not properly configured. Please contact support.'; | |
errorResponse.configError = true; | |
return res.status(500).json(errorResponse); | |
} | |
// 网络相关错误 | |
if (saveError.code === 'ETIMEDOUT' || saveError.message.includes('timeout')) { | |
errorResponse.error = 'Storage operation timeout'; | |
errorResponse.suggestion = 'The save operation took too long. Try reducing file size or try again later.'; | |
errorResponse.timeoutError = true; | |
return res.status(504).json(errorResponse); | |
} | |
// 默认错误 | |
errorResponse.suggestion = 'Unknown save error. Please try again or contact support if the problem persists.'; | |
errorResponse.unknownError = true; | |
return res.status(500).json(errorResponse); | |
} | |
} catch (error) { | |
console.error(`❌ PPT save failed for user ${req.user?.userId}, PPT ID: ${req.body?.pptId}:`, { | |
error: error.message, | |
stack: error.stack, | |
userId: req.user?.userId, | |
pptId: req.body?.pptId, | |
requestSize: req.get('content-length'), | |
slidesCount: req.body?.slides?.length, | |
errorName: error.name, | |
errorCode: error.code | |
}); | |
// 提供更详细的错误处理 | |
let errorResponse = { | |
error: 'PPT save processing failed', | |
details: error.message, | |
timestamp: new Date().toISOString(), | |
userId: req.user?.userId, | |
pptId: req.body?.pptId, | |
architecture: 'folder-based' | |
}; | |
if (error.message.includes('Invalid slide data')) { | |
errorResponse.error = 'Invalid slide data detected'; | |
errorResponse.suggestion = 'One or more slides contain invalid or corrupted data'; | |
return res.status(400).json(errorResponse); | |
} | |
if (error.message.includes('JSON')) { | |
errorResponse.error = 'Invalid data format'; | |
errorResponse.suggestion = 'Check slide data structure and content'; | |
return res.status(400).json(errorResponse); | |
} | |
errorResponse.suggestion = 'Unknown processing error. Please try again or contact support if the problem persists.'; | |
next(error); | |
} | |
}); | |
// 创建新PPT | |
router.post('/create', async (req, res, next) => { | |
try { | |
const userId = req.user.userId; | |
const { title } = req.body; | |
if (!title) { | |
return res.status(400).json({ error: 'Title is required' }); | |
} | |
const pptId = uuidv4(); | |
const now = new Date().toISOString(); | |
// 确保数据格式与前端store一致 | |
const pptData = { | |
id: pptId, | |
title, | |
theme: { | |
backgroundColor: '#ffffff', | |
themeColor: '#d14424', | |
fontColor: '#333333', | |
fontName: 'Microsoft YaHei' | |
}, | |
slides: [ | |
{ | |
id: uuidv4(), | |
elements: [ | |
{ | |
type: 'text', | |
id: uuidv4(), | |
left: 150, | |
top: 200, | |
width: 600, | |
height: 100, | |
content: title, | |
fontSize: 32, | |
fontName: 'Microsoft YaHei', | |
defaultColor: '#333333', | |
bold: true, | |
align: 'center' | |
} | |
], | |
background: { | |
type: 'solid', | |
color: '#ffffff' | |
} | |
} | |
], | |
// 确保视口信息与前端一致 | |
viewportSize: 1000, | |
viewportRatio: 0.5625, | |
createdAt: now, | |
updatedAt: now | |
}; | |
const fileName = `${pptId}.json`; | |
// 优先使用Huggingface存储服务保存PPT数据 | |
const { default: huggingfaceStorageService } = await import('../services/huggingfaceStorageService.js'); | |
try { | |
// 尝试保存到Huggingface存储 | |
await huggingfaceStorageService.storePPTData(userId, pptId, pptData); | |
console.log(`✅ PPT created and saved to Huggingface storage: ${pptId}`); | |
} catch (hfError) { | |
console.warn('⚠️ Huggingface storage unavailable, falling back to GitHub:', hfError.message); | |
// 回退到GitHub存储 | |
console.log(`Creating PPT for user ${userId}, using ${githubService.useMemoryStorage ? 'memory' : 'GitHub'} storage`); | |
if (githubService.useMemoryStorage) { | |
await githubService.saveToMemory(userId, fileName, pptData); | |
} else { | |
await githubService.saveFile(userId, fileName, pptData); | |
} | |
} | |
console.log(`PPT created successfully: ${pptId}`); | |
res.json({ success: true, pptId, pptData }); | |
} catch (error) { | |
console.error('PPT creation error:', error); | |
next(error); | |
} | |
}); | |
// 删除PPT | |
router.delete('/:pptId', async (req, res, next) => { | |
try { | |
const userId = req.user.userId; | |
const { pptId } = req.params; | |
const fileName = `${pptId}.json`; | |
let deleted = false; | |
// 优先从Huggingface存储删除 | |
const { default: huggingfaceStorageService } = await import('../services/huggingfaceStorageService.js'); | |
try { | |
await huggingfaceStorageService.deletePPTData(userId, pptId); | |
deleted = true; | |
console.log(`✅ PPT deleted from Huggingface storage: ${pptId}`); | |
} catch (hfError) { | |
console.warn('⚠️ Failed to delete from Huggingface storage, trying other storage:', hfError.message); | |
if (githubService.useMemoryStorage) { | |
// 内存存储模式 | |
deleted = githubService.memoryStorage.delete(`users/${userId}/${fileName}`); | |
if (deleted) { | |
console.log(`✅ PPT deleted from memory storage: ${pptId}`); | |
} | |
} else { | |
// GitHub存储模式 - 从所有仓库中删除 | |
if (!githubService.repositories || githubService.repositories.length === 0) { | |
console.warn('⚠️ GitHub repositories not configured, cannot delete from GitHub storage'); | |
} else { | |
for (let i = 0; i < githubService.repositories.length; i++) { | |
try { | |
await githubService.deleteFile(userId, fileName, i); | |
deleted = true; | |
console.log(`✅ PPT deleted from GitHub storage: ${pptId}`); | |
break; // 只需要从一个仓库删除成功即可 | |
} catch (error) { | |
// 继续尝试其他仓库 | |
continue; | |
} | |
} | |
} | |
} | |
} | |
if (!deleted) { | |
return res.status(404).json({ error: 'PPT not found in any storage' }); | |
} | |
res.json({ message: 'PPT deleted successfully' }); | |
} catch (error) { | |
next(error); | |
} | |
}); | |
// 复制PPT | |
router.post('/:pptId/copy', async (req, res, next) => { | |
try { | |
const userId = req.user.userId; | |
const { pptId } = req.params; | |
const { title } = req.body; | |
const sourceFileName = `${pptId}.json`; | |
// 获取源PPT数据 - 优先从Huggingface存储读取 | |
let sourcePPT = null; | |
const { default: huggingfaceStorageService } = await import('../services/huggingfaceStorageService.js'); | |
try { | |
// 尝试从Huggingface存储读取 | |
sourcePPT = await huggingfaceStorageService.getPPTData(userId, pptId); | |
console.log('✅ Source PPT loaded from Huggingface storage for copying'); | |
} catch (hfError) { | |
console.warn('⚠️ Failed to load source PPT from Huggingface storage, trying other storage:', hfError.message); | |
if (githubService.useMemoryStorage) { | |
// 内存存储模式 | |
sourcePPT = await githubService.getFromMemory(userId, sourceFileName); | |
if (sourcePPT) { | |
console.log('✅ Source PPT loaded from memory storage for copying'); | |
} | |
} else { | |
// GitHub存储模式 | |
if (!githubService.repositories || githubService.repositories.length === 0) { | |
console.warn('⚠️ GitHub repositories not configured, cannot load from GitHub storage'); | |
} else { | |
for (let i = 0; i < githubService.repositories.length; i++) { | |
try { | |
const result = await githubService.getFile(userId, sourceFileName, i); | |
if (result) { | |
sourcePPT = result; | |
console.log('✅ Source PPT loaded from GitHub storage for copying'); | |
break; | |
} | |
} catch (error) { | |
continue; | |
} | |
} | |
} | |
} | |
} | |
if (!sourcePPT) { | |
return res.status(404).json({ error: 'Source PPT not found' }); | |
} | |
// 创建新的PPT ID和数据 | |
const newPptId = uuidv4(); | |
const newFileName = `${newPptId}.json`; | |
const newPPTData = { | |
...sourcePPT, | |
id: newPptId, | |
title: title || `${sourcePPT.title} - 副本`, | |
createdAt: new Date().toISOString(), | |
updatedAt: new Date().toISOString() | |
}; | |
// 保存复制的PPT - 优先保存到Huggingface存储 | |
try { | |
await huggingfaceStorageService.storePPTData(userId, newPptId, newPPTData); | |
console.log(`✅ Copied PPT saved to Huggingface storage: ${newPptId}`); | |
} catch (hfError) { | |
console.warn('⚠️ Failed to save copied PPT to Huggingface storage, using fallback:', hfError.message); | |
if (githubService.useMemoryStorage) { | |
await githubService.saveToMemory(userId, newFileName, newPPTData); | |
console.log(`✅ Copied PPT saved to memory storage: ${newPptId}`); | |
} else { | |
await githubService.saveFile(userId, newFileName, newPPTData, 0); | |
console.log(`✅ Copied PPT saved to GitHub storage: ${newPptId}`); | |
} | |
} | |
res.json({ | |
message: 'PPT copied successfully', | |
pptId: newPptId, | |
ppt: newPPTData | |
}); | |
} catch (error) { | |
next(error); | |
} | |
}); | |
export default router; |