SOY NV AI commited on
Commit
0276388
·
1 Parent(s): 4a8654b

파일 업로드 타임아웃 증가 및 에러 처리 개선

Browse files

- 파일 업로드 타임아웃을 15분(900초)으로 증가
- progressContainer 스코프 문제 수정
- 에러 메시지 화면 표시 개선

Files changed (2) hide show
  1. app/routes.py +17 -4
  2. templates/admin_webnovels.html +35 -11
app/routes.py CHANGED
@@ -407,7 +407,7 @@ character_relationships는 이 청크에 등장하는 인물들 간의 현재
407
  'num_predict': 500
408
  }
409
  },
410
- timeout=30
411
  )
412
  if ollama_response.status_code == 200:
413
  response_data = ollama_response.json()
@@ -582,11 +582,16 @@ def analyze_episode(episode_content, episode_title, full_content=None, parent_ch
582
  'num_predict': 2000
583
  }
584
  },
585
- timeout=60
586
  )
587
  if ollama_response.status_code == 200:
588
  response_data = ollama_response.json()
589
  return response_data.get('response', '').strip()
 
 
 
 
 
590
  except Exception as e:
591
  print(f"[회차 분석] Ollama 오류: {str(e)}")
592
 
@@ -901,10 +906,18 @@ def create_parent_chunk_with_ai(file_id, content, model_name):
901
  response_data = ollama_response.json()
902
  analysis_result = response_data.get('message', {}).get('content', '')
903
  print(f"[Parent Chunk 생성] Ollama API 응답 수신 성공: {len(analysis_result)}자")
 
 
 
 
 
 
 
 
904
  except requests.exceptions.RequestException as e:
905
- print(f"[Parent Chunk 생성] ❌ Ollama API 연결 오류: {str(e)}")
906
  print(f"[Parent Chunk 생성] 디버그: Ollama URL: {OLLAMA_BASE_URL}")
907
- raise
908
 
909
  if not analysis_result:
910
  print(f"[Parent Chunk 생성] ⚠️ 경고: 분석 결과가 비어있습니다.")
 
407
  'num_predict': 500
408
  }
409
  },
410
+ timeout=120 # 2분 타임아웃
411
  )
412
  if ollama_response.status_code == 200:
413
  response_data = ollama_response.json()
 
582
  'num_predict': 2000
583
  }
584
  },
585
+ timeout=300 # 5분 타임아웃 (회차 분석은 시간이 오래 걸릴 수 있음)
586
  )
587
  if ollama_response.status_code == 200:
588
  response_data = ollama_response.json()
589
  return response_data.get('response', '').strip()
590
+ except requests.exceptions.Timeout:
591
+ print(f"[회차 분석] Ollama 타임아웃: 요청 시간이 초과되었습니다. (5분)")
592
+ print(f"[회차 분석] 회차 내용이 너무 길거나 모델 응답이 느릴 수 있습니다.")
593
+ except requests.exceptions.ConnectionError:
594
+ print(f"[회차 분석] Ollama 연결 오류: Ollama 서버에 연결할 수 없습니다.")
595
  except Exception as e:
596
  print(f"[회차 분석] Ollama 오류: {str(e)}")
597
 
 
906
  response_data = ollama_response.json()
907
  analysis_result = response_data.get('message', {}).get('content', '')
908
  print(f"[Parent Chunk 생성] Ollama API 응답 수신 성공: {len(analysis_result)}자")
909
+ except requests.exceptions.Timeout:
910
+ print(f"[Parent Chunk 생성] ❌ Ollama 타임아웃: 요청 시간이 초과되었습니다. (5분)")
911
+ print(f"[Parent Chunk 생성] 파일이 너무 크거나 모델 응답이 느릴 수 있습니다.")
912
+ return None
913
+ except requests.exceptions.ConnectionError:
914
+ print(f"[Parent Chunk 생성] ❌ Ollama 연결 오류: Ollama 서버에 연결할 수 없습니다.")
915
+ print(f"[Parent Chunk 생성] 디버그: Ollama URL: {OLLAMA_BASE_URL}")
916
+ return None
917
  except requests.exceptions.RequestException as e:
918
+ print(f"[Parent Chunk 생성] ❌ Ollama API 오류: {str(e)}")
919
  print(f"[Parent Chunk 생성] 디버그: Ollama URL: {OLLAMA_BASE_URL}")
920
+ return None
921
 
922
  if not analysis_result:
923
  print(f"[Parent Chunk 생성] ⚠️ 경고: 분석 결과가 비어있습니다.")
templates/admin_webnovels.html CHANGED
@@ -764,6 +764,10 @@
764
  return;
765
  }
766
 
 
 
 
 
767
  try {
768
  // 업로드 중 UI 비활성화
769
  console.log('[handleFileUpload] UI 비활성화 시작');
@@ -776,8 +780,8 @@
776
  fileInput.disabled = true;
777
 
778
  // 진행 상태 초기화
779
- const progressContainer = document.getElementById('fileUploadProgress');
780
- const progressItems = document.getElementById('progressItems');
781
 
782
  if (!progressContainer || !progressItems) {
783
  console.error('[handleFileUpload] 진행 상태 컨테이너를 찾을 수 없습니다');
@@ -886,12 +890,12 @@
886
  console.log(`[단계 1] fetch 호출 시작: /api/upload`);
887
  console.log(`[단계 1] FormData 항목:`, Array.from(formData.entries()).map(([k, v]) => [k, v instanceof File ? v.name : v]));
888
 
889
- // 타임아웃이 있는 fetch 래퍼
890
- const fetchWithTimeout = (url, options, timeout = 300000) => { // 5분 타임아웃
891
  return Promise.race([
892
  fetch(url, options),
893
  new Promise((_, reject) =>
894
- setTimeout(() => reject(new Error(`요청 타임아웃: ${timeout/1000}초 내에 응답이 없습니다.`)), timeout)
895
  )
896
  ]);
897
  };
@@ -900,7 +904,7 @@
900
  method: 'POST',
901
  body: formData,
902
  credentials: 'include' // 쿠키 포함 (세션 인증)
903
- }, 300000); // 5분 타임아웃
904
 
905
  console.log(`[단계 1] fetch 응답 수신: ${response.status} ${response.statusText}`);
906
 
@@ -994,13 +998,29 @@
994
  console.error(`[업로드 예외] 파일: ${file.name}`, error);
995
  console.error(`[업로드 예외 스택]`, error.stack);
996
 
997
- // 네트워크 오류나 타임아웃인 경우 사용자에게 명확한 메시지 표시
998
- if (error.name === 'TypeError' && error.message.includes('fetch')) {
 
 
 
 
 
 
 
 
 
 
 
999
  console.error(`[네트워크 오류] 서버와의 연결이 끊어졌습니다.`);
1000
  if (fileUploadStatus) {
1001
  fileUploadStatus.textContent = `[${i + 1}/${files.length}] 네트워크 오류: 서버 연결 실패`;
1002
  fileUploadStatus.className = 'file-upload-status error';
1003
  }
 
 
 
 
 
1004
  }
1005
  console.error(`[스택 트레이스]`, error.stack);
1006
  }
@@ -1031,7 +1051,7 @@
1031
  } else {
1032
  fileUploadStatus.textContent = '모든 파일 업로드 실패';
1033
  fileUploadStatus.className = 'file-upload-status error';
1034
- const errorDetails = errors.length > 0 ? '\n' + errors.slice(0, 3).join('\n') + (errors.length > 3 ? `\n... 외 ${errors.length - 3}개 오류` : '') : '';
1035
  showAlert(`파일 업로드에 실패했습니다.${errorDetails}`, 'error');
1036
  }
1037
  }
@@ -1049,8 +1069,12 @@
1049
 
1050
  // 3초 후 진행 상태 숨기기
1051
  setTimeout(() => {
1052
- progressContainer.classList.remove('active');
1053
- fileUploadStatus.textContent = '';
 
 
 
 
1054
  }, 3000);
1055
  }
1056
 
 
764
  return;
765
  }
766
 
767
+ // 진행 상태 컨테이너를 함수 상단에서 선언 (스코프 문제 해결)
768
+ let progressContainer = null;
769
+ let progressItems = null;
770
+
771
  try {
772
  // 업로드 중 UI 비활성화
773
  console.log('[handleFileUpload] UI 비활성화 시작');
 
780
  fileInput.disabled = true;
781
 
782
  // 진행 상태 초기화
783
+ progressContainer = document.getElementById('fileUploadProgress');
784
+ progressItems = document.getElementById('progressItems');
785
 
786
  if (!progressContainer || !progressItems) {
787
  console.error('[handleFileUpload] 진행 상태 컨테이너를 찾을 수 없습니다');
 
890
  console.log(`[단계 1] fetch 호출 시작: /api/upload`);
891
  console.log(`[단계 1] FormData 항목:`, Array.from(formData.entries()).map(([k, v]) => [k, v instanceof File ? v.name : v]));
892
 
893
+ // 타임아웃이 있는 fetch 래퍼 (15분 타임아웃 - 큰 파일 처리 시간 고려)
894
+ const fetchWithTimeout = (url, options, timeout = 900000) => { // 15분 타임아웃
895
  return Promise.race([
896
  fetch(url, options),
897
  new Promise((_, reject) =>
898
+ setTimeout(() => reject(new Error(`요청 타임아웃: ${timeout/1000}초(${Math.floor(timeout/60000)}분) 내에 응답이 없습니다. 파일이 크거나 처리 시간이 오래 걸릴 수 있습니다.`)), timeout)
899
  )
900
  ]);
901
  };
 
904
  method: 'POST',
905
  body: formData,
906
  credentials: 'include' // 쿠키 포함 (세션 인증)
907
+ }, 900000); // 15분 타임아웃
908
 
909
  console.log(`[단계 1] fetch 응답 수신: ${response.status} ${response.statusText}`);
910
 
 
998
  console.error(`[업로드 예외] 파일: ${file.name}`, error);
999
  console.error(`[업로드 예외 스택]`, error.stack);
1000
 
1001
+ // 타임아웃 오류인 경우 사용자에게 명확한 메시지 표시
1002
+ if (error.message && error.message.includes('타임아웃')) {
1003
+ const timeoutMsg = `파일 업로드 타임아웃: ${file.name}\n파일이 크거나 처리 시간이 오래 걸려 타임아웃이 발생했습니다.`;
1004
+ console.error(`[타임아웃 오류] ${timeoutMsg}`);
1005
+ if (fileUploadStatus) {
1006
+ fileUploadStatus.textContent = `[${i + 1}/${files.length}] 타임아웃: ${file.name}`;
1007
+ fileUploadStatus.className = 'file-upload-status error';
1008
+ }
1009
+ // 사용자에게 알림 표시
1010
+ showAlert(timeoutMsg, 'error');
1011
+ }
1012
+ // 네트워크 오류인 경우 사용자에게 명확한 메시지 표시
1013
+ else if (error.name === 'TypeError' && error.message.includes('fetch')) {
1014
  console.error(`[네트워크 오류] 서버와의 연결이 끊어졌습니다.`);
1015
  if (fileUploadStatus) {
1016
  fileUploadStatus.textContent = `[${i + 1}/${files.length}] 네트워크 오류: 서버 연결 실패`;
1017
  fileUploadStatus.className = 'file-upload-status error';
1018
  }
1019
+ showAlert(`네트워크 오류: 서버와의 연결이 끊어졌습니다. (${file.name})`, 'error');
1020
+ }
1021
+ // 기타 오류도 사용자에게 표시
1022
+ else {
1023
+ showAlert(`파일 업로드 오류: ${file.name}\n${errorMsg}`, 'error');
1024
  }
1025
  console.error(`[스택 트레이스]`, error.stack);
1026
  }
 
1051
  } else {
1052
  fileUploadStatus.textContent = '모든 파일 업로드 실패';
1053
  fileUploadStatus.className = 'file-upload-status error';
1054
+ const errorDetails = errors.length > 0 ? '\n\n오류 상세:\n' + errors.slice(0, 5).map((e, idx) => `${idx + 1}. ${e}`).join('\n') + (errors.length > 5 ? `\n... 외 ${errors.length - 5}개 오류` : '') : '';
1055
  showAlert(`파일 업로드에 실패했습니다.${errorDetails}`, 'error');
1056
  }
1057
  }
 
1069
 
1070
  // 3초 후 진행 상태 숨기기
1071
  setTimeout(() => {
1072
+ if (progressContainer) {
1073
+ progressContainer.classList.remove('active');
1074
+ }
1075
+ if (fileUploadStatus) {
1076
+ fileUploadStatus.textContent = '';
1077
+ }
1078
  }, 3000);
1079
  }
1080