laster 0.0.2
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from email.message import Message
|
||||
import re
|
||||
from pathlib import Path
|
||||
from urllib.parse import unquote, urlparse
|
||||
@@ -14,10 +15,39 @@ from app.utils.validators import validate_url_host
|
||||
SAFE_FILE_RE = re.compile(r"[^a-zA-Z0-9._+-]+")
|
||||
|
||||
|
||||
def _safe_file_name(url: str) -> str:
|
||||
def _sanitize_file_name(value: str, fallback: str = "package.deb") -> str:
|
||||
name = SAFE_FILE_RE.sub("-", value).strip("-.")
|
||||
return name or fallback
|
||||
|
||||
|
||||
def _safe_url_file_name(url: str) -> str:
|
||||
parsed = urlparse(url)
|
||||
name = Path(unquote(parsed.path)).name or "package.deb"
|
||||
return SAFE_FILE_RE.sub("-", name).strip("-") or "package.deb"
|
||||
if name == "download":
|
||||
parts = [part for part in parsed.path.split("/") if part]
|
||||
name = "-".join(parts[-3:]) if len(parts) >= 3 else name
|
||||
return _sanitize_file_name(name)
|
||||
|
||||
|
||||
def _content_disposition_file_name(value: str) -> str:
|
||||
if not value:
|
||||
return ""
|
||||
|
||||
message = Message()
|
||||
message["content-disposition"] = value
|
||||
return _sanitize_file_name(message.get_filename() or "", fallback="")
|
||||
|
||||
|
||||
def _response_file_name(url: str, response: httpx.Response) -> str:
|
||||
name = _content_disposition_file_name(response.headers.get("content-disposition", ""))
|
||||
if not name:
|
||||
name = _safe_url_file_name(str(response.url) or url)
|
||||
|
||||
content_type = response.headers.get("content-type", "").split(";", 1)[0].strip().lower()
|
||||
if content_type == "application/vnd.debian.binary-package" and not name.lower().endswith(".deb"):
|
||||
name = f"{name}.deb"
|
||||
|
||||
return name
|
||||
|
||||
|
||||
class Downloader:
|
||||
@@ -28,12 +58,12 @@ class Downloader:
|
||||
def download(self, url: str) -> Path:
|
||||
validate_url_host(url, settings.allowed_download_hosts)
|
||||
settings.cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
destination = settings.cache_dir / _safe_file_name(url)
|
||||
|
||||
self.repository.add_log(self.task_id, "info", f"Downloading {url}")
|
||||
with httpx.stream("GET", url, follow_redirects=True, timeout=120) as response:
|
||||
response.raise_for_status()
|
||||
self._validate_response(url, response)
|
||||
destination = settings.cache_dir / _response_file_name(url, response)
|
||||
with destination.open("wb") as handle:
|
||||
for chunk in response.iter_bytes():
|
||||
handle.write(chunk)
|
||||
|
||||
Reference in New Issue
Block a user