format: 格式化代码

This commit is contained in:
Wenjie Zhang 2026-01-29 21:08:43 +08:00
parent 5a376dd1b9
commit 812bb95f8d
7 changed files with 176 additions and 125 deletions

View File

@ -727,6 +727,7 @@ async def process_url_to_markdown(url: str, params: dict | None = None) -> str:
try: try:
import httpx import httpx
# 使用异步 HTTP 客户端获取页面 # 使用异步 HTTP 客户端获取页面
async with httpx.AsyncClient(timeout=30.0, follow_redirects=True) as client: async with httpx.AsyncClient(timeout=30.0, follow_redirects=True) as client:
response = await client.get(url, headers={"User-Agent": "Mozilla/5.0"}) response = await client.get(url, headers={"User-Agent": "Mozilla/5.0"})

View File

@ -1,8 +1,10 @@
import httpx
from urllib.parse import urlparse
import socket
import ipaddress import ipaddress
from src.knowledge.utils.url_validator import validate_url, is_url_parsing_enabled import socket
from urllib.parse import urlparse
import httpx
from src.knowledge.utils.url_validator import is_url_parsing_enabled, validate_url
from src.utils import logger from src.utils import logger
# 最大允许下载大小 (例如 10MB) # 最大允许下载大小 (例如 10MB)
@ -10,9 +12,11 @@ MAX_DOWNLOAD_SIZE = 10 * 1024 * 1024
# 允许的 Content-Type # 允许的 Content-Type
ALLOWED_CONTENT_TYPES = ["text/html", "application/xhtml+xml"] ALLOWED_CONTENT_TYPES = ["text/html", "application/xhtml+xml"]
async def is_private_ip(hostname: str) -> bool: async def is_private_ip(hostname: str) -> bool:
"""Check if the hostname resolves to a private IP address.""" """Check if the hostname resolves to a private IP address."""
import asyncio import asyncio
try: try:
# Resolve hostname to IP in a separate thread to avoid blocking the event loop # Resolve hostname to IP in a separate thread to avoid blocking the event loop
ip_list = await asyncio.to_thread(socket.getaddrinfo, hostname, None) ip_list = await asyncio.to_thread(socket.getaddrinfo, hostname, None)
@ -29,6 +33,7 @@ async def is_private_ip(hostname: str) -> bool:
# For now, we return False assuming standard DNS failure handling. # For now, we return False assuming standard DNS failure handling.
return False return False
async def fetch_url_content(url: str, max_size: int = MAX_DOWNLOAD_SIZE) -> tuple[bytes, str]: async def fetch_url_content(url: str, max_size: int = MAX_DOWNLOAD_SIZE) -> tuple[bytes, str]:
""" """
Fetch URL content with security checks (size limit, content type, private IP blocking). Fetch URL content with security checks (size limit, content type, private IP blocking).
@ -68,7 +73,12 @@ async def fetch_url_content(url: str, max_size: int = MAX_DOWNLOAD_SIZE) -> tupl
# Request headers # Request headers
headers = { headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" "User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/91.0.4472.124 "
"Safari/537.36"
)
} }
# Stream the response to check headers before downloading body # Stream the response to check headers before downloading body
@ -92,6 +102,7 @@ async def fetch_url_content(url: str, max_size: int = MAX_DOWNLOAD_SIZE) -> tupl
parsed_current = urlparse(current_url) parsed_current = urlparse(current_url)
# simple join # simple join
from urllib.parse import urljoin from urllib.parse import urljoin
current_url = urljoin(current_url, location) current_url = urljoin(current_url, location)
else: else:
current_url = location current_url = location

View File

@ -1,4 +1,5 @@
"""URL validation utilities for whitelist-based URL parsing.""" """URL validation utilities for whitelist-based URL parsing."""
import os import os
from urllib.parse import urlparse from urllib.parse import urlparse

42
uv.lock
View File

@ -559,6 +559,15 @@ wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" },
] ]
[[package]]
name = "cssselect"
version = "1.4.0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/2e/cdfd8b01c37cbf4f9482eefd455853a3cf9c995029a46acd31dfaa9c1dd6/cssselect-1.4.0.tar.gz", hash = "sha256:fdaf0a1425e17dfe8c5cf66191d211b357cf7872ae8afc4c6762ddd8ac47fc92", size = 40589, upload-time = "2026-01-29T07:00:26.701Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/0c/7bb51e3acfafd16c48875bf3db03607674df16f5b6ef8d056586af7e2b8b/cssselect-1.4.0-py3-none-any.whl", hash = "sha256:c0ec5c0191c8ee39fcc8afc1540331d8b55b0183478c50e9c8a79d44dbceb1d8", size = 18540, upload-time = "2026-01-29T07:00:24.994Z" },
]
[[package]] [[package]]
name = "dashscope" name = "dashscope"
version = "1.25.8" version = "1.25.8"
@ -2109,6 +2118,23 @@ wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/ef/dcf1d29c3f530577f61e5fe2f1bd72929acf779953668a8a47a479ae6f26/lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf", size = 3671248, upload-time = "2025-09-22T04:02:27.918Z" }, { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/ef/dcf1d29c3f530577f61e5fe2f1bd72929acf779953668a8a47a479ae6f26/lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf", size = 3671248, upload-time = "2025-09-22T04:02:27.918Z" },
] ]
[package.optional-dependencies]
html-clean = [
{ name = "lxml-html-clean" },
]
[[package]]
name = "lxml-html-clean"
version = "0.4.3"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "lxml" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d9/cb/c9c5bb2a9c47292e236a808dd233a03531f53b626f36259dcd32b49c76da/lxml_html_clean-0.4.3.tar.gz", hash = "sha256:c9df91925b00f836c807beab127aac82575110eacff54d0a75187914f1bd9d8c", size = 21498, upload-time = "2025-10-02T20:49:24.895Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/10/4a/63a9540e3ca73709f4200564a737d63a4c8c9c4dd032bab8535f507c190a/lxml_html_clean-0.4.3-py3-none-any.whl", hash = "sha256:63fd7b0b9c3a2e4176611c2ca5d61c4c07ffca2de76c14059a81a2825833731e", size = 14177, upload-time = "2025-10-02T20:49:23.749Z" },
]
[[package]] [[package]]
name = "markdown-it-py" name = "markdown-it-py"
version = "4.0.0" version = "4.0.0"
@ -3603,6 +3629,20 @@ wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/12/1e5497183bdbe782dbb91bad1d0d2297dba4d2831b2652657f7517bfc6df/rapidocr_onnxruntime-1.4.4-py3-none-any.whl", hash = "sha256:971d7d5f223a7a808662229df1ef69893809d8457d834e6373d3854bc1782cbf", size = 14915192, upload-time = "2025-01-17T01:48:25.104Z" }, { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ba/12/1e5497183bdbe782dbb91bad1d0d2297dba4d2831b2652657f7517bfc6df/rapidocr_onnxruntime-1.4.4-py3-none-any.whl", hash = "sha256:971d7d5f223a7a808662229df1ef69893809d8457d834e6373d3854bc1782cbf", size = 14915192, upload-time = "2025-01-17T01:48:25.104Z" },
] ]
[[package]]
name = "readability-lxml"
version = "0.8.4.1"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "chardet" },
{ name = "cssselect" },
{ name = "lxml", extra = ["html-clean"] },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/55/3e/dc87d97532ddad58af786ec89c7036182e352574c1cba37bf2bf783d2b15/readability_lxml-0.8.4.1.tar.gz", hash = "sha256:9d2924f5942dd7f37fb4da353263b22a3e877ccf922d0e45e348e4177b035a53", size = 22874, upload-time = "2025-05-03T21:11:45.493Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/75/2cc58965097e351415af420be81c4665cf80da52a17ef43c01ffbe2caf91/readability_lxml-0.8.4.1-py3-none-any.whl", hash = "sha256:874c0cea22c3bf2b78c7f8df831bfaad3c0a89b7301d45a188db581652b4b465", size = 19912, upload-time = "2025-05-03T21:11:43.993Z" },
]
[[package]] [[package]]
name = "referencing" name = "referencing"
version = "0.37.0" version = "0.37.0"
@ -4956,6 +4996,7 @@ dependencies = [
{ name = "python-multipart" }, { name = "python-multipart" },
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "rapidocr-onnxruntime" }, { name = "rapidocr-onnxruntime" },
{ name = "readability-lxml" },
{ name = "rich" }, { name = "rich" },
{ name = "sqlalchemy", extra = ["asyncio"] }, { name = "sqlalchemy", extra = ["asyncio"] },
{ name = "tabulate" }, { name = "tabulate" },
@ -5033,6 +5074,7 @@ requires-dist = [
{ name = "python-multipart", specifier = ">=0.0.20" }, { name = "python-multipart", specifier = ">=0.0.20" },
{ name = "pyyaml", specifier = ">=6.0.2" }, { name = "pyyaml", specifier = ">=6.0.2" },
{ name = "rapidocr-onnxruntime", specifier = ">=1.4.4" }, { name = "rapidocr-onnxruntime", specifier = ">=1.4.4" },
{ name = "readability-lxml", specifier = ">=0.8.1" },
{ name = "rich", specifier = ">=13.7.1" }, { name = "rich", specifier = ">=13.7.1" },
{ name = "sqlalchemy", extras = ["asyncio"], specifier = ">=2.0.0" }, { name = "sqlalchemy", extras = ["asyncio"], specifier = ">=2.0.0" },
{ name = "tabulate", specifier = ">=0.9.0" }, { name = "tabulate", specifier = ">=0.9.0" },

View File

@ -8,11 +8,7 @@
<!-- 错误状态 --> <!-- 错误状态 -->
<div v-else-if="error" class="error-container"> <div v-else-if="error" class="error-container">
<a-result <a-result status="error" :title="error.title" :sub-title="error.message">
status="error"
:title="error.title"
:sub-title="error.message"
>
<template #extra> <template #extra>
<a-button type="primary" @click="retryLoad">重试</a-button> <a-button type="primary" @click="retryLoad">重试</a-button>
</template> </template>