- 新增jd模块基础路由,整合app_store和payment子路由 - 实现苹果权益充值接口,支持苹果、携程及沃尔玛多个渠道 - 实现卡号密码查询接口,支持不同类别订单查询 - 新增短信认证相关接口,实现短信验证码发送及短信登录 - 新增商品管理接口,支持SKU详情查询及账号类下单功能 - 新增订单管理接口,实现订单删除功能 - 实现支付相关接口,增加刷新支付参数功能 - 定义完整请求及响应数据模型,确保接口数据规范 - 编写AppStoreSpider类,封装苹果应用内订单处理逻辑 - 引入多种代理池及请求重试机制,增强接口稳定性 - 添加详细日志记录,便于请求追踪与错误排查
178 lines
6.5 KiB
Python
178 lines
6.5 KiB
Python
"""
|
|
Core configuration management using Pydantic Settings.
|
|
All configuration loaded from environment variables with validation.
|
|
"""
|
|
|
|
from enum import StrEnum
|
|
from typing import Literal
|
|
from pydantic import Field, field_validator
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
class ProxyPoolType(StrEnum):
|
|
"""代理池类型枚举"""
|
|
|
|
DEFAULT = "default" # 默认代理池
|
|
EXPIRING = "expiring" # 带有效期的代理池
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""
|
|
Application settings with environment variable support.
|
|
|
|
Configuration is loaded from environment variables and .env files.
|
|
All settings are validated at startup using Pydantic validators.
|
|
"""
|
|
|
|
model_config = SettingsConfigDict(
|
|
env_file=".env",
|
|
env_file_encoding="utf-8",
|
|
case_sensitive=False,
|
|
extra="ignore",
|
|
)
|
|
|
|
# Application Settings
|
|
app_name: str = Field(default="kami_spider", description="Application name")
|
|
environment: Literal["development", "staging", "production"] = Field(
|
|
default="development", description="Runtime environment"
|
|
)
|
|
debug: bool = Field(default=False, description="Debug mode")
|
|
host: str = Field(default="0.0.0.0", description="Server host")
|
|
port: int = Field(default=8000, description="Server port")
|
|
workers: int = Field(default=1, description="Number of worker processes")
|
|
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = Field(
|
|
default="INFO", description="Logging level"
|
|
)
|
|
|
|
# Database Settings
|
|
db_host: str = Field(default="localhost", description="MySQL host")
|
|
db_port: int = Field(default=3306, description="MySQL port")
|
|
db_name: str = Field(default="kami_spider", description="Database name")
|
|
db_user: str = Field(default="root", description="Database user")
|
|
db_password: str = Field(default="", description="Database password")
|
|
db_pool_size: int = Field(default=10, description="Database connection pool size")
|
|
db_max_overflow: int = Field(
|
|
default=20, description="Database max overflow connections"
|
|
)
|
|
db_pool_recycle: int = Field(
|
|
default=3600, description="Database pool recycle time in seconds"
|
|
)
|
|
db_pool_pre_ping: bool = Field(
|
|
default=True, description="Test connections before using"
|
|
)
|
|
db_echo: bool = Field(default=False, description="Echo SQL statements")
|
|
|
|
# Redis Settings
|
|
redis_host: str = Field(default="localhost", description="Redis host")
|
|
redis_port: int = Field(default=6379, description="Redis port")
|
|
redis_db: int = Field(default=0, description="Redis database number")
|
|
redis_password: str = Field(default="", description="Redis password")
|
|
redis_max_connections: int = Field(
|
|
default=50, description="Redis connection pool max connections"
|
|
)
|
|
redis_decode_responses: bool = Field(
|
|
default=True, description="Decode Redis responses to strings"
|
|
)
|
|
|
|
# OpenTelemetry Settings
|
|
otel_enabled: bool = Field(default=True, description="Enable OpenTelemetry")
|
|
otel_service_name: str = Field(
|
|
default="kami_spider", description="Service name for traces"
|
|
)
|
|
otel_exporter_endpoint: str = Field(
|
|
default="38.38.251.113:31547",
|
|
description="OpenTelemetry collector gRPC endpoint",
|
|
)
|
|
otel_exporter_insecure: bool = Field(
|
|
default=True, description="Use insecure gRPC connection"
|
|
)
|
|
otel_sample_rate: float = Field(
|
|
default=1.0, description="Trace sampling rate (0.0 to 1.0)"
|
|
)
|
|
|
|
# CORS Settings
|
|
cors_enabled: bool = Field(default=True, description="Enable CORS")
|
|
cors_allow_origins: list[str] = Field(
|
|
default=["*"], description="Allowed CORS origins"
|
|
)
|
|
cors_allow_credentials: bool = Field(
|
|
default=True, description="Allow credentials in CORS"
|
|
)
|
|
cors_allow_methods: list[str] = Field(
|
|
default=["*"], description="Allowed HTTP methods"
|
|
)
|
|
cors_allow_headers: list[str] = Field(
|
|
default=["*"], description="Allowed HTTP headers"
|
|
)
|
|
|
|
# 代理设置
|
|
proxy_enable: bool = Field(default=True, description="是否启用代理")
|
|
proxy_url: str = Field(
|
|
default="https://share.proxy.qg.net/get?key=7ASQH2BI&num=1&area=&isp=0&format=txt&seq=\r\n&distinct=false&area=510100",
|
|
description="代理服务器地址",
|
|
)
|
|
proxy_type: ProxyPoolType = Field(
|
|
default=ProxyPoolType.DEFAULT, description="代理服务器类型"
|
|
)
|
|
proxy_username: str = Field(default="7ASQH2BI", description="代理服务器用户名")
|
|
proxy_password: str = Field(default="34D6652FE7B6", description="代理服务器密码")
|
|
|
|
# Security Settings
|
|
secret_key: str = Field(
|
|
default="change-me-in-production", description="Secret key for signing tokens"
|
|
)
|
|
|
|
@field_validator("workers")
|
|
@classmethod
|
|
def validate_workers(cls, v: int) -> int:
|
|
"""Ensure workers is at least 1."""
|
|
if v < 1:
|
|
raise ValueError("workers must be at least 1")
|
|
return v
|
|
|
|
@field_validator("otel_sample_rate")
|
|
@classmethod
|
|
def validate_sample_rate(cls, v: float) -> float:
|
|
"""Ensure sample rate is between 0.0 and 1.0."""
|
|
if not 0.0 <= v <= 1.0:
|
|
raise ValueError("otel_sample_rate must be between 0.0 and 1.0")
|
|
return v
|
|
|
|
@property
|
|
def database_url(self) -> str:
|
|
"""Generate async database URL for SQLModel/SQLAlchemy."""
|
|
password_part = f":{self.db_password}" if self.db_password else ""
|
|
return (
|
|
f"mysql+aiomysql://{self.db_user}{password_part}"
|
|
f"@{self.db_host}:{self.db_port}/{self.db_name}"
|
|
)
|
|
|
|
@property
|
|
def sync_database_url(self) -> str:
|
|
"""Generate sync database URL for Alembic migrations."""
|
|
password_part = f":{self.db_password}" if self.db_password else ""
|
|
return (
|
|
f"mysql+pymysql://{self.db_user}{password_part}"
|
|
f"@{self.db_host}:{self.db_port}/{self.db_name}"
|
|
)
|
|
|
|
@property
|
|
def redis_url(self) -> str:
|
|
"""Generate Redis URL."""
|
|
password_part = f":{self.redis_password}@" if self.redis_password else ""
|
|
return f"redis://{password_part}{self.redis_host}:{self.redis_port}/{self.redis_db}"
|
|
|
|
@property
|
|
def is_production(self) -> bool:
|
|
"""Check if running in production environment."""
|
|
return self.environment == "production"
|
|
|
|
@property
|
|
def is_development(self) -> bool:
|
|
"""Check if running in development environment."""
|
|
return self.environment == "development"
|
|
|
|
|
|
# Global settings instance
|
|
settings = Settings()
|