workspace— step2
This commit is contained in:
@@ -22,7 +22,7 @@ from app.models.scrape import (
|
|||||||
SearchRequest,
|
SearchRequest,
|
||||||
SearchResultData, TradeMonitorRequest,
|
SearchResultData, TradeMonitorRequest,
|
||||||
)
|
)
|
||||||
from app.utils.http_utils import generate_signed_headers
|
from surugaya_common.http_utils import generate_signed_headers
|
||||||
from app.utils.parse_util import (
|
from app.utils.parse_util import (
|
||||||
get_franchise_shipping_fee,
|
get_franchise_shipping_fee,
|
||||||
get_handle_fee,
|
get_handle_fee,
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ from app.models.scrape import CategoryData, DetailRequest, ProductDetailData, Pr
|
|||||||
from app.services.browser_pool import BrowserPool
|
from app.services.browser_pool import BrowserPool
|
||||||
from app.services.cloudflare_session import CloudflareSessionManager
|
from app.services.cloudflare_session import CloudflareSessionManager
|
||||||
from app.services.session_store import SessionStore
|
from app.services.session_store import SessionStore
|
||||||
from app.utils.app_utils import is_empty_str
|
|
||||||
from app.utils.parse_util import format_price, get_shipping_fee, surugaya_photo_url_to_cdn
|
from app.utils.parse_util import format_price, get_shipping_fee, surugaya_photo_url_to_cdn
|
||||||
|
from surugaya_common.app_utils import is_empty_str
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,12 @@ description = "Shared contracts (Redis keys, future models) for jp_surugaya serv
|
|||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pydantic>=2.0.0,<3.0.0",
|
"pydantic>=2.0.0,<3.0.0",
|
||||||
"redis>=5.0.0,<6.0.0",
|
"redis==8.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["src/surugaya_common"]
|
packages = ["src/surugaya_common"]
|
||||||
|
|
||||||
# NOTE (deferred): redis pin here intentionally matches the API service (<6).
|
# Keep surugaya_common light: stdlib + pydantic + redis only. redis is pinned to
|
||||||
# The worker's redis==8 requirement is NOT introduced in this increment, so there
|
# ==8.0.0 to stay in lockstep with both the API service and the worker. Do not add
|
||||||
# is no conflict to resolve yet. Do not add fastapi/playwright/opencv/numpy/
|
# fastapi/playwright/opencv/numpy/pyautogui/pillow here.
|
||||||
# pyautogui/pillow here — surugaya_common must stay light.
|
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
|
"""通用字符串工具(stdlib only)。
|
||||||
|
|
||||||
|
从 app/utils/app_utils.py 迁移而来,作为 surugaya_common 的共享实现,
|
||||||
|
供 API 服务与 worker 复用。
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def is_valid_str(s):
|
def is_valid_str(s):
|
||||||
"""判断字符串是否有效,即非空且非空格。"""
|
"""判断字符串是否有效,即非空且非空格。"""
|
||||||
return isinstance(s, str) and len(s.strip()) > 0
|
return isinstance(s, str) and len(s.strip()) > 0
|
||||||
|
|
||||||
|
|
||||||
def is_empty_str(s):
|
def is_empty_str(s):
|
||||||
"""判断字符串是否为空,即空或仅包含空格。"""
|
"""判断字符串是否为空,即空或仅包含空格。"""
|
||||||
return isinstance(s, str) and len(s.strip()) == 0
|
return isinstance(s, str) and len(s.strip()) == 0
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
"""HTTP 签名工具(stdlib only)。
|
||||||
|
|
||||||
|
从 app/utils/http_utils.py 迁移而来,逻辑与 worker 端实现一致,
|
||||||
|
作为 surugaya_common 的共享实现统一维护。
|
||||||
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import hmac
|
import hmac
|
||||||
@@ -5,6 +11,7 @@ import secrets
|
|||||||
import hashlib
|
import hashlib
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
|
||||||
def generate_signed_headers(app_id: str, app_secret: str, data: Dict[str, Any], method: str = "POST") -> Dict[str, str]:
|
def generate_signed_headers(app_id: str, app_secret: str, data: Dict[str, Any], method: str = "POST") -> Dict[str, str]:
|
||||||
timestamp = str(int(time.time() * 1000))
|
timestamp = str(int(time.time() * 1000))
|
||||||
nonce = secrets.token_hex(16)
|
nonce = secrets.token_hex(16)
|
||||||
+1
-1
@@ -10,7 +10,7 @@ dependencies = [
|
|||||||
"playwright-stealth>=1.0.0,<3.0.0",
|
"playwright-stealth>=1.0.0,<3.0.0",
|
||||||
"pydantic>=2.0.0,<3.0.0",
|
"pydantic>=2.0.0,<3.0.0",
|
||||||
"pydantic-settings>=2.4.0,<3.0.0",
|
"pydantic-settings>=2.4.0,<3.0.0",
|
||||||
"redis>=5.0.0,<6.0.0",
|
"redis==8.0.0",
|
||||||
"selectolax>=0.3.21,<1.0.0",
|
"selectolax>=0.3.21,<1.0.0",
|
||||||
"starlette>=0.37.0,<1.0.0",
|
"starlette>=0.37.0,<1.0.0",
|
||||||
"uvicorn[standard]>=0.30.0,<1.0.0",
|
"uvicorn[standard]>=0.30.0,<1.0.0",
|
||||||
|
|||||||
@@ -366,15 +366,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pyjwt"
|
|
||||||
version = "2.12.1"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
version = "8.4.2"
|
version = "8.4.2"
|
||||||
@@ -460,14 +451,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redis"
|
name = "redis"
|
||||||
version = "5.3.1"
|
version = "8.0.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
sdist = { url = "https://files.pythonhosted.org/packages/53/ae/ed461cca5780b5fc8b9fe8ca0ed98d89508645fb9d880c24cc42c087678f/redis-8.0.0.tar.gz", hash = "sha256:a00c5355432051ac14e593b8b197fc76c887ee12d55a0984f69328a1115fdc49", size = 5101591, upload-time = "2026-05-28T12:45:13.5Z" }
|
||||||
{ name = "pyjwt" },
|
|
||||||
]
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/6a/cf/128b1b6d7086200c9f387bd4be9b2572a30b90745ef078bd8b235042dc9f/redis-5.3.1.tar.gz", hash = "sha256:ca49577a531ea64039b5a36db3d6cd1a0c7a60c34124d46924a45b956e8cf14c", size = 4626200, upload-time = "2025-07-25T08:06:27.778Z" }
|
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/7f/26/5c5fa0e83c3621db835cfc1f1d789b37e7fa99ed54423b5f519beb931aa7/redis-5.3.1-py3-none-any.whl", hash = "sha256:dc1909bd24669cc31b5f67a039700b16ec30571096c5f1f0d9d2324bff31af97", size = 272833, upload-time = "2025-07-25T08:06:26.317Z" },
|
{ url = "https://files.pythonhosted.org/packages/27/e3/b519734372d305bd547534a9f32e4ce9f98552af753dce72cf3483a0ff0b/redis-8.0.0-py3-none-any.whl", hash = "sha256:c938c18338585009f0bc310f4c7e4e4b4d37639356c4ac072cedf3af570c8dc7", size = 499870, upload-time = "2026-05-28T12:45:11.697Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -539,7 +527,7 @@ dependencies = [
|
|||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "pydantic", specifier = ">=2.0.0,<3.0.0" },
|
{ name = "pydantic", specifier = ">=2.0.0,<3.0.0" },
|
||||||
{ name = "redis", specifier = ">=5.0.0,<6.0.0" },
|
{ name = "redis", specifier = "==8.0.0" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -576,7 +564,7 @@ requires-dist = [
|
|||||||
{ name = "pydantic-settings", specifier = ">=2.4.0,<3.0.0" },
|
{ name = "pydantic-settings", specifier = ">=2.4.0,<3.0.0" },
|
||||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.0,<9.0.0" },
|
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.0,<9.0.0" },
|
||||||
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0,<1.0.0" },
|
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0,<1.0.0" },
|
||||||
{ name = "redis", specifier = ">=5.0.0,<6.0.0" },
|
{ name = "redis", specifier = "==8.0.0" },
|
||||||
{ name = "selectolax", specifier = ">=0.3.21,<1.0.0" },
|
{ name = "selectolax", specifier = ">=0.3.21,<1.0.0" },
|
||||||
{ name = "starlette", specifier = ">=0.37.0,<1.0.0" },
|
{ name = "starlette", specifier = ">=0.37.0,<1.0.0" },
|
||||||
{ name = "surugaya-common", editable = "packages/surugaya_common" },
|
{ name = "surugaya-common", editable = "packages/surugaya_common" },
|
||||||
|
|||||||
Reference in New Issue
Block a user