SOY NV AI commited on
Commit
4b16f0b
Β·
1 Parent(s): 7f732cd

Fix: Resolve file path mismatch on live server by adding get_safe_path utility and safe_file_path property

Browse files
app/database.py CHANGED
@@ -55,6 +55,12 @@ class UploadedFile(db.Model):
55
  # 관계
56
  parent_file = db.relationship('UploadedFile', remote_side=[id], backref='child_files')
57
 
 
 
 
 
 
 
58
  def to_dict(self):
59
  # 청크 개수 계산
60
  chunk_count = len(self.chunks) if hasattr(self, 'chunks') else 0
 
55
  # 관계
56
  parent_file = db.relationship('UploadedFile', remote_side=[id], backref='child_files')
57
 
58
+ @property
59
+ def safe_file_path(self):
60
+ """ν™˜κ²½ 변화에 λŒ€μ‘ν•˜λŠ” μ•ˆμ „ν•œ 파일 경둜 λ°˜ν™˜"""
61
+ from app.utils.file_utils import get_safe_path
62
+ return get_safe_path(self.file_path)
63
+
64
  def to_dict(self):
65
  # 청크 개수 계산
66
  chunk_count = len(self.chunks) if hasattr(self, 'chunks') else 0
app/routes.py CHANGED
@@ -12770,8 +12770,9 @@ def create_file_parent_chunk(file_id):
12770
  return jsonify({'error': 'Parent ChunkλŠ” ν…μŠ€νŠΈ 파일(.txt, .md)μ—λ§Œ 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.'}), 400
12771
 
12772
  # 파일 경둜 확인
12773
- if not file.file_path or not os.path.exists(file.file_path):
12774
- error_msg = f'파일 κ²½λ‘œκ°€ μœ νš¨ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€: {file.file_path}'
 
12775
  print(f"[Parent Chunk 생성] 였λ₯˜: {error_msg}")
12776
  return jsonify({'error': error_msg}), 500
12777
 
@@ -12779,13 +12780,13 @@ def create_file_parent_chunk(file_id):
12779
  try:
12780
  encoding = 'utf-8'
12781
  try:
12782
- with open(file.file_path, 'r', encoding=encoding) as f:
12783
  content = f.read()
12784
  except UnicodeDecodeError:
12785
- with open(file.file_path, 'r', encoding='cp949') as f:
12786
  content = f.read()
12787
  except FileNotFoundError:
12788
- error_msg = f'νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€: {file.file_path}'
12789
  print(f"[Parent Chunk 생성] 였λ₯˜: {error_msg}")
12790
  return jsonify({'error': error_msg}), 500
12791
  except PermissionError:
@@ -12855,15 +12856,16 @@ def process_chunks(file_id):
12855
 
12856
  # 파일 λ‚΄μš© 읽기
12857
  try:
 
12858
  encoding = 'utf-8'
12859
  try:
12860
- with open(file.file_path, 'r', encoding=encoding) as f:
12861
  content = f.read()
12862
  except UnicodeDecodeError:
12863
- with open(file.file_path, 'r', encoding='cp949') as f:
12864
  content = f.read()
12865
  except Exception as e:
12866
- return jsonify({'error': f'νŒŒμΌμ„ 읽을 수 μ—†μŠ΅λ‹ˆλ‹€: {str(e)}'}), 500
12867
 
12868
  print(f"[단계 2: Chunk 생성] 파일 ID {file_id}에 λŒ€ν•œ Chunk 생성 μ‹œμž‘")
12869
  chunk_count = create_chunks_for_file(file_id, content, skip_episode_analysis=True, skip_graph_extraction=True)
 
12770
  return jsonify({'error': 'Parent ChunkλŠ” ν…μŠ€νŠΈ 파일(.txt, .md)μ—λ§Œ 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.'}), 400
12771
 
12772
  # 파일 경둜 확인
12773
+ safe_path = file.safe_file_path
12774
+ if not safe_path or not os.path.exists(safe_path):
12775
+ error_msg = f'νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€ (DB경둜: {file.file_path}, μ‹œλ„κ²½λ‘œ: {safe_path})'
12776
  print(f"[Parent Chunk 생성] 였λ₯˜: {error_msg}")
12777
  return jsonify({'error': error_msg}), 500
12778
 
 
12780
  try:
12781
  encoding = 'utf-8'
12782
  try:
12783
+ with open(safe_path, 'r', encoding=encoding) as f:
12784
  content = f.read()
12785
  except UnicodeDecodeError:
12786
+ with open(safe_path, 'r', encoding='cp949') as f:
12787
  content = f.read()
12788
  except FileNotFoundError:
12789
+ error_msg = f'νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€: {safe_path}'
12790
  print(f"[Parent Chunk 생성] 였λ₯˜: {error_msg}")
12791
  return jsonify({'error': error_msg}), 500
12792
  except PermissionError:
 
12856
 
12857
  # 파일 λ‚΄μš© 읽기
12858
  try:
12859
+ safe_path = file.safe_file_path
12860
  encoding = 'utf-8'
12861
  try:
12862
+ with open(safe_path, 'r', encoding=encoding) as f:
12863
  content = f.read()
12864
  except UnicodeDecodeError:
12865
+ with open(safe_path, 'r', encoding='cp949') as f:
12866
  content = f.read()
12867
  except Exception as e:
12868
+ return jsonify({'error': f'νŒŒμΌμ„ 읽을 수 μ—†μŠ΅λ‹ˆλ‹€: {str(e)} (경둜: {file.file_path})'}), 500
12869
 
12870
  print(f"[단계 2: Chunk 생성] 파일 ID {file_id}에 λŒ€ν•œ Chunk 생성 μ‹œμž‘")
12871
  chunk_count = create_chunks_for_file(file_id, content, skip_episode_analysis=True, skip_graph_extraction=True)
app/services/style_analysis_service.py CHANGED
@@ -31,7 +31,7 @@ class StyleAnalysisService:
31
  if not file_record:
32
  raise ValueError(f"File {file_id} not found")
33
 
34
- text_content = self._read_file_content(file_record.file_path)
35
  # ν…μŠ€νŠΈκ°€ 없어도 DB μ‘°νšŒλŠ” μ‹œλ„ν•΄μ•Ό 함 (이미 μ €μž₯된 경우)
36
 
37
  if part == "bible":
@@ -58,14 +58,15 @@ class StyleAnalysisService:
58
 
59
  def _read_file_content(self, file_path: str) -> str:
60
  """파일 읽기 및 μ „μ²˜λ¦¬"""
 
61
  try:
62
- if not os.path.exists(file_path):
63
- file_path = os.path.join(os.getcwd(), file_path)
64
 
65
- if not os.path.exists(file_path):
 
66
  return ""
67
 
68
- with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
69
  return f.read()
70
  except Exception as e:
71
  logger.error(f"Error reading file {file_path}: {e}")
 
31
  if not file_record:
32
  raise ValueError(f"File {file_id} not found")
33
 
34
+ text_content = self._read_file_content(file_record.safe_file_path)
35
  # ν…μŠ€νŠΈκ°€ 없어도 DB μ‘°νšŒλŠ” μ‹œλ„ν•΄μ•Ό 함 (이미 μ €μž₯된 경우)
36
 
37
  if part == "bible":
 
58
 
59
  def _read_file_content(self, file_path: str) -> str:
60
  """파일 읽기 및 μ „μ²˜λ¦¬"""
61
+ from app.utils.file_utils import get_safe_path
62
  try:
63
+ safe_path = get_safe_path(file_path)
 
64
 
65
+ if not os.path.exists(safe_path):
66
+ logger.warning(f"File not found: {file_path} (Safe path tried: {safe_path})")
67
  return ""
68
 
69
+ with open(safe_path, 'r', encoding='utf-8', errors='ignore') as f:
70
  return f.read()
71
  except Exception as e:
72
  logger.error(f"Error reading file {file_path}: {e}")
app/utils/file_utils.py CHANGED
@@ -2,6 +2,7 @@
2
  파일 κ΄€λ ¨ μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜
3
  """
4
 
 
5
  from pathlib import Path
6
  from typing import Optional
7
  from werkzeug.utils import secure_filename
@@ -12,6 +13,39 @@ from app.core.logger import get_logger
12
  logger = get_logger(__name__)
13
 
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  def allowed_file(filename: str) -> bool:
16
  """
17
  파일 ν™•μž₯μžκ°€ ν—ˆμš©λœ ν™•μž₯μžμΈμ§€ 확인
 
2
  파일 κ΄€λ ¨ μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜
3
  """
4
 
5
+ import os
6
  from pathlib import Path
7
  from typing import Optional
8
  from werkzeug.utils import secure_filename
 
13
  logger = get_logger(__name__)
14
 
15
 
16
+ def get_safe_path(stored_path: str) -> str:
17
+ """
18
+ DB에 μ €μž₯된 파일 κ²½λ‘œκ°€ ν˜„μž¬ ν™˜κ²½μ—μ„œ μœ νš¨ν•˜μ§€ μ•Šμ„ 경우,
19
+ ν˜„μž¬ UPLOAD_FOLDER κΈ°μ€€μœΌλ‘œ λ³΄μ •λœ 경둜λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
20
+ """
21
+ if not stored_path:
22
+ return ""
23
+
24
+ # 1. 원본 κ²½λ‘œκ°€ μ‘΄μž¬ν•˜λ©΄ κ·ΈλŒ€λ‘œ λ°˜ν™˜
25
+ if os.path.exists(stored_path):
26
+ return stored_path
27
+
28
+ # 2. 파일λͺ…λ§Œ μΆ”μΆœν•˜μ—¬ ν˜„μž¬ μ„€μ •λœ UPLOAD_FOLDERμ—μ„œ μ°ΎκΈ°
29
+ filename = os.path.basename(stored_path)
30
+ new_path = os.path.join(str(Config.UPLOAD_FOLDER), filename)
31
+
32
+ if os.path.exists(new_path):
33
+ return new_path
34
+
35
+ # 3. /app/uploads/ -> /data/uploads/ λ³€ν™˜ μ‹œλ„ (HF/Railway 일반적인 μΌ€μ΄μŠ€)
36
+ if '/app/uploads/' in stored_path:
37
+ alt_path = stored_path.replace('/app/uploads/', str(Config.UPLOAD_FOLDER) + '/')
38
+ if os.path.exists(alt_path):
39
+ return alt_path
40
+
41
+ # 4. μƒλŒ€ 경둜 μ‹œλ„
42
+ rel_path = os.path.join(os.getcwd(), stored_path)
43
+ if os.path.exists(rel_path):
44
+ return rel_path
45
+
46
+ return stored_path # μ°Ύμ§€ λͺ»ν•˜λ©΄ 일단 원본 λ°˜ν™˜ (μ—λŸ¬ μ²˜λ¦¬λŠ” ν˜ΈμΆœλΆ€μ—μ„œ)
47
+
48
+
49
  def allowed_file(filename: str) -> bool:
50
  """
51
  파일 ν™•μž₯μžκ°€ ν—ˆμš©λœ ν™•μž₯μžμΈμ§€ 확인