File size: 22,129 Bytes
b7560a4
 
 
 
 
 
e0dffb4
 
 
 
 
 
b7560a4
 
 
 
0afb612
b7560a4
 
 
 
 
 
 
 
 
 
 
0afb612
 
b7560a4
 
0afb612
b7560a4
0afb612
 
 
 
 
 
 
 
 
 
 
 
 
b7560a4
0afb612
 
 
b7560a4
0afb612
 
b7560a4
 
 
0afb612
b7560a4
 
 
 
 
 
0afb612
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7560a4
 
0afb612
 
 
 
 
 
 
 
 
 
 
 
 
 
b7560a4
0afb612
b7560a4
 
 
 
0afb612
b7560a4
 
 
ac4b97d
b7560a4
0afb612
 
 
 
 
 
 
 
 
 
b7560a4
0afb612
b7560a4
 
 
0afb612
 
 
 
 
 
 
b7560a4
 
 
0afb612
 
 
 
 
 
 
 
 
 
 
 
 
 
b7560a4
 
 
 
0afb612
 
 
b7560a4
0afb612
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7560a4
 
0afb612
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7560a4
0afb612
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7560a4
 
 
 
 
 
 
 
 
eeee843
 
 
 
b7560a4
 
eeee843
b7560a4
0afb612
b7560a4
 
eeee843
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0afb612
 
 
eeee843
 
b7560a4
 
eeee843
b7560a4
0afb612
eeee843
0afb612
 
 
 
b7560a4
0afb612
 
 
b7560a4
eeee843
b7560a4
 
 
 
 
 
 
 
 
 
 
0afb612
 
 
 
 
 
 
 
 
 
b7560a4
0afb612
 
 
b7560a4
 
 
 
 
0afb612
 
 
 
b7560a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0afb612
 
 
 
 
 
 
b7560a4
0afb612
b7560a4
0afb612
b7560a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0afb612
 
b7560a4
0afb612
b7560a4
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
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列表
router.get('/list', async (req, res, next) => {
  try {
    const userId = req.user.userId;
    const pptList = await githubService.getUserPPTList(userId);
    res.json(pptList);
  } catch (error) {
    next(error);
  }
});

// 获取指定PPT数据
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;
    
    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;
      
      console.log(`💾 Using 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;
      }

      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`;
    
    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`;

    if (githubService.useMemoryStorage) {
      // 内存存储模式
      const deleted = githubService.memoryStorage.delete(`users/${userId}/${fileName}`);
      if (!deleted) {
        return res.status(404).json({ error: 'PPT not found' });
      }
    } else {
      // GitHub存储模式 - 从所有仓库中删除
      let deleted = false;
      for (let i = 0; i < githubService.repositories.length; i++) {
        try {
          await githubService.deleteFile(userId, fileName, i);
          deleted = true;
          break; // 只需要从一个仓库删除成功即可
        } catch (error) {
          // 继续尝试其他仓库
          continue;
        }
      }
      
      if (!deleted) {
        return res.status(404).json({ error: 'PPT not found in any repository' });
      }
    }

    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数据
    let sourcePPT = null;
    
    if (githubService.useMemoryStorage) {
      // 内存存储模式
      sourcePPT = await githubService.getFromMemory(userId, sourceFileName);
    } else {
      // GitHub存储模式
      for (let i = 0; i < githubService.repositories.length; i++) {
        try {
          const result = await githubService.getFile(userId, sourceFileName, i);
          if (result) {
            sourcePPT = result;
            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
    if (githubService.useMemoryStorage) {
      await githubService.saveToMemory(userId, newFileName, newPPTData);
    } else {
      await githubService.saveFile(userId, newFileName, newPPTData, 0);
    }

    res.json({ 
      message: 'PPT copied successfully', 
      pptId: newPptId,
      ppt: newPPTData
    });
  } catch (error) {
    next(error);
  }
});

export default router;