원격 전체 통합: B07 CAD 도각 분리·유역도 정리·도면층 국문화 + 용화 실무문서 #5

Merged
eomsangdon merged 70 commits from feat/B07-cad-block-library into main 2026-08-31 17:33:10 +09:00
Showing only changes of commit b59d0e4f2c - Show all commits
+13 -7
View File
@@ -60,8 +60,9 @@ def resolve_temp_batch_path(user_id: int, batch_id: str, *, create: bool = True)
if not batch_id or any(sep in batch_id for sep in ("/", "\\", "..")):
raise ValueError("임시 보관함 묶음 식별자가 올바르지 않습니다.")
temp_root = os.path.abspath(os.path.join(STORAGE_BASE_DIR, TEMP_UPLOAD_DIR_NAME))
path = os.path.abspath(os.path.join(temp_root, str(user_id), batch_id))
# 실경로로 맞춘다 — 저장 엔진이 `Path.resolve()`를 쓰므로 기준이 같아야 한다.
temp_root = os.path.realpath(os.path.join(STORAGE_BASE_DIR, TEMP_UPLOAD_DIR_NAME))
path = os.path.realpath(os.path.join(temp_root, str(user_id), batch_id))
if os.path.commonpath((temp_root, path)) != temp_root or path == temp_root:
raise ValueError("임시 보관함 경로가 보관함 루트를 벗어났습니다.")
if create:
@@ -70,8 +71,8 @@ def resolve_temp_batch_path(user_id: int, batch_id: str, *, create: bool = True)
def temp_upload_root() -> str:
"""임시 보관함 루트(`storage/tmp`) 절대 경로."""
return os.path.abspath(os.path.join(STORAGE_BASE_DIR, TEMP_UPLOAD_DIR_NAME))
"""임시 보관함 루트(`storage/tmp`) 실경로."""
return os.path.realpath(os.path.join(STORAGE_BASE_DIR, TEMP_UPLOAD_DIR_NAME))
def resolve_project_root_for_delete(relative_path: str, project_id: str) -> str:
@@ -100,15 +101,20 @@ def resolve_project_root_for_delete(relative_path: str, project_id: str) -> str:
def resolve_stored_project_path(relative_path: str) -> str:
"""DB의 storage 기준 상대 경로를 검증해 실제 프로젝트 경로로 변환한다."""
"""DB의 storage 기준 상대 경로를 검증해 실제 프로젝트 경로로 변환한다.
실경로(`realpath`)로 돌려준다. `storage/`가 심볼릭 링크·정션일 수 있고(워크트리를
나눠 쓰면 실제로 그렇다), 저장 엔진 쪽은 `Path.resolve()`로 링크를 따라간다. 두
경로의 기준이 다르면 `chunk_path.relative_to(project_root)`가 터진다.
"""
normalized = PurePosixPath(relative_path.replace("\\", "/"))
if normalized.is_absolute() or ".." in normalized.parts:
raise ValueError("프로젝트 저장 경로는 안전한 상대 경로여야 합니다.")
if not normalized.parts or normalized.parts[0] != "storage":
raise ValueError("프로젝트 저장 경로는 storage/로 시작해야 합니다.")
storage_root = os.path.abspath(STORAGE_BASE_DIR)
path = os.path.abspath(os.path.join(storage_root, *normalized.parts[1:]))
storage_root = os.path.realpath(STORAGE_BASE_DIR)
path = os.path.realpath(os.path.join(storage_root, *normalized.parts[1:]))
if os.path.commonpath((storage_root, path)) != storage_root or path == storage_root:
raise ValueError("프로젝트 저장 경로가 저장소 루트를 벗어났습니다.")
os.makedirs(path, exist_ok=True)