CatPtain commited on
Commit
e7e1ca2
·
verified ·
1 Parent(s): cd3995d

Upload githubService.js

Browse files
Files changed (1) hide show
  1. backend/src/services/githubService.js +201 -109
backend/src/services/githubService.js CHANGED
@@ -57,6 +57,7 @@ class GitHubService {
57
 
58
  // 测试是否可以访问仓库内容
59
  let canAccessContents = false;
 
60
  try {
61
  await axios.get(`${this.apiUrl}/repos/${owner}/${repo}/contents`, {
62
  headers: {
@@ -67,6 +68,12 @@ class GitHubService {
67
  canAccessContents = true;
68
  } catch (contentsError) {
69
  console.log(`Cannot access repository contents: ${contentsError.message}`);
 
 
 
 
 
 
70
  }
71
 
72
  repoResults.push({
@@ -75,7 +82,9 @@ class GitHubService {
75
  name: repoResponse.data.full_name,
76
  permissions,
77
  canAccessContents,
78
- defaultBranch: repoResponse.data.default_branch
 
 
79
  });
80
  } catch (error) {
81
  console.error(`Repository access error for ${repoUrl}:`, error.response?.data || error.message);
@@ -97,6 +106,65 @@ class GitHubService {
97
  return { owner: match[1], repo: match[2] };
98
  }
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  // 获取文件内容
101
  async getFile(userId, fileName, repoIndex = 0) {
102
  // 如果使用内存存储
@@ -190,126 +258,150 @@ class GitHubService {
190
  } catch (error) {
191
  console.error(`GitHub save failed:`, error.response?.data || error.message);
192
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  // 详细的错误处理
194
  if (error.response?.status === 404) {
195
- console.log('404 error - attempting to create directory structure...');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
- try {
198
- // 方法1: 先检查仓库是否存在和可访问
199
- const repoCheckResponse = await axios.get(`${this.apiUrl}/repos/${owner}/${repo}`, {
 
 
 
 
 
 
 
 
 
200
  headers: {
201
  'Authorization': `token ${this.token}`,
202
  'Accept': 'application/vnd.github.v3+json'
203
  }
204
- });
205
- console.log(`Repository exists: ${repoCheckResponse.data.full_name}`);
206
-
207
- // 方法2: 检查users目录是否存在
208
- try {
209
- await axios.get(`${this.apiUrl}/repos/${owner}/${repo}/contents/users`, {
210
- headers: {
211
- 'Authorization': `token ${this.token}`,
212
- 'Accept': 'application/vnd.github.v3+json'
213
- }
214
- });
215
- console.log('Users directory exists');
216
- } catch (usersDirError) {
217
- console.log('Users directory does not exist, creating...');
218
-
219
- // 创建users目录的README
220
- const usersReadmePath = 'users/README.md';
221
- const usersReadmeContent = Buffer.from('# Users Directory\n\nThis directory contains user-specific PPT files.\n').toString('base64');
222
-
223
- await axios.put(
224
- `${this.apiUrl}/repos/${owner}/${repo}/contents/${usersReadmePath}`,
225
- {
226
- message: 'Create users directory',
227
- content: usersReadmeContent,
228
- branch: 'main'
229
- },
230
- {
231
- headers: {
232
- 'Authorization': `token ${this.token}`,
233
- 'Accept': 'application/vnd.github.v3+json'
234
- }
235
- }
236
- );
237
- console.log('Users directory created');
238
  }
239
-
240
- // 方法3: 检查用户目录是否存在
241
- try {
242
- await axios.get(`${this.apiUrl}/repos/${owner}/${repo}/contents/users/${userId}`, {
243
- headers: {
244
- 'Authorization': `token ${this.token}`,
245
- 'Accept': 'application/vnd.github.v3+json'
246
- }
247
- });
248
- console.log(`User directory exists: users/${userId}`);
249
- } catch (userDirError) {
250
- console.log(`User directory does not exist, creating: users/${userId}`);
251
-
252
- // 创建用户目录的README
253
- const userReadmePath = `users/${userId}/README.md`;
254
- const userReadmeContent = Buffer.from(`# PPT Files for User ${userId}\n\nThis directory contains PPT files for user ${userId}.\n`).toString('base64');
255
-
256
- await axios.put(
257
- `${this.apiUrl}/repos/${owner}/${repo}/contents/${userReadmePath}`,
258
- {
259
- message: `Create user directory for ${userId}`,
260
- content: userReadmeContent,
261
- branch: 'main'
262
- },
263
- {
264
- headers: {
265
- 'Authorization': `token ${this.token}`,
266
- 'Accept': 'application/vnd.github.v3+json'
267
- }
268
- }
269
- );
270
- console.log(`User directory created: users/${userId}`);
271
  }
272
-
273
- // 等待一小会儿让GitHub同步
274
- await new Promise(resolve => setTimeout(resolve, 1000));
275
-
276
- // 重试保存PPT文件
277
- console.log('Retrying PPT file save...');
278
- const retryResponse = await axios.put(
279
- `${this.apiUrl}/repos/${owner}/${repo}/contents/${path}`,
280
- payload,
281
- {
282
- headers: {
283
- 'Authorization': `token ${this.token}`,
284
- 'Accept': 'application/vnd.github.v3+json'
285
- }
 
 
 
 
 
 
286
  }
287
- );
288
-
289
- console.log(`Successfully saved to GitHub after retry: ${retryResponse.data.commit.sha}`);
290
- return retryResponse.data;
291
-
292
- } catch (retryError) {
293
- console.error(`Comprehensive retry also failed:`, retryError.response?.data || retryError.message);
294
-
295
- // 如果GitHub彻底失败,fallback到内存存储
296
- console.log('GitHub save completely failed, falling back to memory storage...');
297
- try {
298
- const memoryResult = await memoryStorageService.saveFile(userId, fileName, data);
299
- console.log('Successfully saved to memory storage as fallback');
300
- return {
301
- ...memoryResult,
302
- warning: 'Saved to temporary memory storage due to GitHub issues'
303
- };
304
- } catch (memoryError) {
305
- console.error('Memory storage fallback also failed:', memoryError.message);
306
- throw new Error(`All storage methods failed. GitHub: ${retryError.message}, Memory: ${memoryError.message}`);
307
  }
308
  }
309
- } else if (error.response?.status === 403) {
310
- throw new Error(`GitHub permission denied. Check if the token has 'repo' permissions: ${error.response.data.message}`);
311
- } else {
312
- throw new Error(`GitHub API error (${error.response?.status}): ${error.response?.data?.message || error.message}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  }
314
  }
315
  }
 
57
 
58
  // 测试是否可以访问仓库内容
59
  let canAccessContents = false;
60
+ let repoEmpty = false;
61
  try {
62
  await axios.get(`${this.apiUrl}/repos/${owner}/${repo}/contents`, {
63
  headers: {
 
68
  canAccessContents = true;
69
  } catch (contentsError) {
70
  console.log(`Cannot access repository contents: ${contentsError.message}`);
71
+
72
+ // 如果是409错误,说明仓库为空
73
+ if (contentsError.response?.status === 409) {
74
+ repoEmpty = true;
75
+ console.log('Repository appears to be empty, will initialize when needed');
76
+ }
77
  }
78
 
79
  repoResults.push({
 
82
  name: repoResponse.data.full_name,
83
  permissions,
84
  canAccessContents,
85
+ repoEmpty,
86
+ defaultBranch: repoResponse.data.default_branch,
87
+ size: repoResponse.data.size
88
  });
89
  } catch (error) {
90
  console.error(`Repository access error for ${repoUrl}:`, error.response?.data || error.message);
 
106
  return { owner: match[1], repo: match[2] };
107
  }
108
 
109
+ // 初始化空仓库
110
+ async initializeRepository(repoIndex = 0) {
111
+ if (this.useMemoryStorage) {
112
+ return { success: false, reason: 'Using memory storage' };
113
+ }
114
+
115
+ try {
116
+ const repoUrl = this.repositories[repoIndex];
117
+ const { owner, repo } = this.parseRepoUrl(repoUrl);
118
+
119
+ console.log(`Initializing empty repository: ${owner}/${repo}`);
120
+
121
+ // 创建初始README文件
122
+ const readmeContent = Buffer.from(`# PPTist Data Repository
123
+
124
+ This repository stores PPT data for the PPTist application.
125
+
126
+ ## Structure
127
+
128
+ \`\`\`
129
+ users/
130
+ ├── PS01/
131
+ │ ├── README.md
132
+ │ └── *.json (PPT files)
133
+ ├── PS02/
134
+ │ ├── README.md
135
+ │ └── *.json (PPT files)
136
+ └── ...
137
+ \`\`\`
138
+
139
+ ## Auto-generated
140
+
141
+ This repository is automatically managed by PPTist Huggingface Space.
142
+ Do not manually edit files unless you know what you're doing.
143
+ `).toString('base64');
144
+
145
+ const response = await axios.put(
146
+ `${this.apiUrl}/repos/${owner}/${repo}/contents/README.md`,
147
+ {
148
+ message: 'Initialize repository for PPTist data storage',
149
+ content: readmeContent,
150
+ branch: 'main'
151
+ },
152
+ {
153
+ headers: {
154
+ 'Authorization': `token ${this.token}`,
155
+ 'Accept': 'application/vnd.github.v3+json'
156
+ }
157
+ }
158
+ );
159
+
160
+ console.log(`Repository initialized successfully: ${response.data.commit.sha}`);
161
+ return { success: true, commit: response.data.commit.sha };
162
+ } catch (error) {
163
+ console.error('Repository initialization failed:', error.response?.data || error.message);
164
+ return { success: false, error: error.message };
165
+ }
166
+ }
167
+
168
  // 获取文件内容
169
  async getFile(userId, fileName, repoIndex = 0) {
170
  // 如果使用内存存储
 
258
  } catch (error) {
259
  console.error(`GitHub save failed:`, error.response?.data || error.message);
260
 
261
+ // 特殊处理空仓库的情况
262
+ if (error.response?.status === 409 && error.response?.data?.message?.includes('empty')) {
263
+ console.log('Repository is empty, initializing...');
264
+ const initResult = await this.initializeRepository(repoIndex);
265
+
266
+ if (initResult.success) {
267
+ console.log('Repository initialized, retrying save...');
268
+ // 等待一会儿让GitHub同步
269
+ await new Promise(resolve => setTimeout(resolve, 2000));
270
+
271
+ // 继续执行原来的目录创建逻辑
272
+ return this.saveFileWithDirectoryCreation(userId, fileName, data, repoIndex, payload);
273
+ }
274
+ }
275
+
276
  // 详细的错误处理
277
  if (error.response?.status === 404) {
278
+ return this.saveFileWithDirectoryCreation(userId, fileName, data, repoIndex, payload);
279
+ } else if (error.response?.status === 403) {
280
+ throw new Error(`GitHub permission denied. Check if the token has 'repo' permissions: ${error.response.data.message}`);
281
+ } else {
282
+ throw new Error(`GitHub API error (${error.response?.status}): ${error.response?.data?.message || error.message}`);
283
+ }
284
+ }
285
+ }
286
+
287
+ // 带目录创建的保存文件方法
288
+ async saveFileWithDirectoryCreation(userId, fileName, data, repoIndex, payload) {
289
+ const repoUrl = this.repositories[repoIndex];
290
+ const { owner, repo } = this.parseRepoUrl(repoUrl);
291
+ const path = `users/${userId}/${fileName}`;
292
+
293
+ console.log('404 error - attempting to create directory structure...');
294
+
295
+ try {
296
+ // 方法1: 先检查仓库是否存在和可访问
297
+ const repoCheckResponse = await axios.get(`${this.apiUrl}/repos/${owner}/${repo}`, {
298
+ headers: {
299
+ 'Authorization': `token ${this.token}`,
300
+ 'Accept': 'application/vnd.github.v3+json'
301
+ }
302
+ });
303
+ console.log(`Repository exists: ${repoCheckResponse.data.full_name}`);
304
+
305
+ // 方法2: 检查users目录是否存在
306
+ try {
307
+ await axios.get(`${this.apiUrl}/repos/${owner}/${repo}/contents/users`, {
308
+ headers: {
309
+ 'Authorization': `token ${this.token}`,
310
+ 'Accept': 'application/vnd.github.v3+json'
311
+ }
312
+ });
313
+ console.log('Users directory exists');
314
+ } catch (usersDirError) {
315
+ console.log('Users directory does not exist, creating...');
316
 
317
+ // 创建users目录的README
318
+ const usersReadmePath = 'users/README.md';
319
+ const usersReadmeContent = Buffer.from('# Users Directory\n\nThis directory contains user-specific PPT files.\n').toString('base64');
320
+
321
+ await axios.put(
322
+ `${this.apiUrl}/repos/${owner}/${repo}/contents/${usersReadmePath}`,
323
+ {
324
+ message: 'Create users directory',
325
+ content: usersReadmeContent,
326
+ branch: 'main'
327
+ },
328
+ {
329
  headers: {
330
  'Authorization': `token ${this.token}`,
331
  'Accept': 'application/vnd.github.v3+json'
332
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
  }
334
+ );
335
+ console.log('Users directory created');
336
+ }
337
+
338
+ // 方法3: 检查用户目录是否存在
339
+ try {
340
+ await axios.get(`${this.apiUrl}/repos/${owner}/${repo}/contents/users/${userId}`, {
341
+ headers: {
342
+ 'Authorization': `token ${this.token}`,
343
+ 'Accept': 'application/vnd.github.v3+json'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  }
345
+ });
346
+ console.log(`User directory exists: users/${userId}`);
347
+ } catch (userDirError) {
348
+ console.log(`User directory does not exist, creating: users/${userId}`);
349
+
350
+ // 创建用户目录的README
351
+ const userReadmePath = `users/${userId}/README.md`;
352
+ const userReadmeContent = Buffer.from(`# PPT Files for User ${userId}\n\nThis directory contains PPT files for user ${userId}.\n`).toString('base64');
353
+
354
+ await axios.put(
355
+ `${this.apiUrl}/repos/${owner}/${repo}/contents/${userReadmePath}`,
356
+ {
357
+ message: `Create user directory for ${userId}`,
358
+ content: userReadmeContent,
359
+ branch: 'main'
360
+ },
361
+ {
362
+ headers: {
363
+ 'Authorization': `token ${this.token}`,
364
+ 'Accept': 'application/vnd.github.v3+json'
365
  }
366
+ }
367
+ );
368
+ console.log(`User directory created: users/${userId}`);
369
+ }
370
+
371
+ // 等待一小会儿让GitHub同步
372
+ await new Promise(resolve => setTimeout(resolve, 1000));
373
+
374
+ // 重试保存PPT文件
375
+ console.log('Retrying PPT file save...');
376
+ const retryResponse = await axios.put(
377
+ `${this.apiUrl}/repos/${owner}/${repo}/contents/${path}`,
378
+ payload,
379
+ {
380
+ headers: {
381
+ 'Authorization': `token ${this.token}`,
382
+ 'Accept': 'application/vnd.github.v3+json'
 
 
 
383
  }
384
  }
385
+ );
386
+
387
+ console.log(`Successfully saved to GitHub after retry: ${retryResponse.data.commit.sha}`);
388
+ return retryResponse.data;
389
+
390
+ } catch (retryError) {
391
+ console.error(`Comprehensive retry also failed:`, retryError.response?.data || retryError.message);
392
+
393
+ // 如果GitHub彻底失败,fallback到内存存储
394
+ console.log('GitHub save completely failed, falling back to memory storage...');
395
+ try {
396
+ const memoryResult = await memoryStorageService.saveFile(userId, fileName, data);
397
+ console.log('Successfully saved to memory storage as fallback');
398
+ return {
399
+ ...memoryResult,
400
+ warning: 'Saved to temporary memory storage due to GitHub issues'
401
+ };
402
+ } catch (memoryError) {
403
+ console.error('Memory storage fallback also failed:', memoryError.message);
404
+ throw new Error(`All storage methods failed. GitHub: ${retryError.message}, Memory: ${memoryError.message}`);
405
  }
406
  }
407
  }