diff --git a/config/setting.toml b/config/setting.toml index 3a8de72..1a7fd80 100644 --- a/config/setting.toml +++ b/config/setting.toml @@ -64,6 +64,10 @@ timezone_offset = 8 # beta测试,目前仍处于测试阶段 # POW 计算模式:local(本地计算)或 external(外部服务) mode = "external" +# 是否使用对应 token 进行 POW 计算(默认关闭) +# local 模式开启后会使用当前轮询 token 获取 POW +# external 模式开启后会向外部服务传递 accesstoken 字段 +use_token_for_pow = false # 外部 POW 服务地址(仅在 external 模式下使用) server_url = "http://localhost:8002" # 外部 POW 服务访问密钥(仅在 external 模式下使用) diff --git a/src/api/admin.py b/src/api/admin.py index eaae03e..288d4f8 100644 --- a/src/api/admin.py +++ b/src/api/admin.py @@ -173,6 +173,7 @@ class UpdatePowProxyConfigRequest(BaseModel): class UpdatePowServiceConfigRequest(BaseModel): mode: str # "local" or "external" + use_token_for_pow: Optional[bool] = False server_url: Optional[str] = None api_key: Optional[str] = None proxy_enabled: Optional[bool] = None @@ -1408,6 +1409,7 @@ async def update_pow_proxy_config( config_obj = await db.get_pow_service_config() await db.update_pow_service_config( mode=config_obj.mode, + use_token_for_pow=config_obj.use_token_for_pow, server_url=config_obj.server_url, api_key=config_obj.api_key, proxy_enabled=request.pow_proxy_enabled, @@ -1432,6 +1434,7 @@ async def get_pow_service_config(token: str = Depends(verify_admin_token)) -> di "success": True, "config": { "mode": config_obj.mode, + "use_token_for_pow": config_obj.use_token_for_pow, "server_url": config_obj.server_url or "", "api_key": config_obj.api_key or "", "proxy_enabled": config_obj.proxy_enabled, @@ -1448,6 +1451,7 @@ async def update_pow_service_config( try: await db.update_pow_service_config( mode=request.mode, + use_token_for_pow=request.use_token_for_pow or False, server_url=request.server_url, api_key=request.api_key, proxy_enabled=request.proxy_enabled, @@ -1455,6 +1459,7 @@ async def update_pow_service_config( ) # Update runtime config config.set_pow_service_mode(request.mode) + config.set_pow_service_use_token_for_pow(request.use_token_for_pow or False) config.set_pow_service_server_url(request.server_url or "") config.set_pow_service_api_key(request.api_key or "") config.set_pow_service_proxy_enabled(request.proxy_enabled or False) diff --git a/src/core/config.py b/src/core/config.py index b97c978..2739a97 100644 --- a/src/core/config.py +++ b/src/core/config.py @@ -285,6 +285,17 @@ class Config: self._config["pow_service"] = {} self._config["pow_service"]["mode"] = mode + @property + def pow_service_use_token_for_pow(self) -> bool: + """Whether to use current token for POW calculation""" + return self._config.get("pow_service", {}).get("use_token_for_pow", False) + + def set_pow_service_use_token_for_pow(self, enabled: bool): + """Set whether to use current token for POW calculation""" + if "pow_service" not in self._config: + self._config["pow_service"] = {} + self._config["pow_service"]["use_token_for_pow"] = enabled + @property def pow_service_server_url(self) -> str: """Get POW service server URL""" diff --git a/src/core/database.py b/src/core/database.py index af3ed19..6edcc6b 100644 --- a/src/core/database.py +++ b/src/core/database.py @@ -230,6 +230,7 @@ class Database: if count[0] == 0: # Get POW service config from config_dict if provided, otherwise use defaults mode = "local" + use_token_for_pow = False server_url = None api_key = None proxy_enabled = False @@ -238,6 +239,7 @@ class Database: if config_dict: pow_service_config = config_dict.get("pow_service", {}) mode = pow_service_config.get("mode", "local") + use_token_for_pow = pow_service_config.get("use_token_for_pow", False) server_url = pow_service_config.get("server_url", "") api_key = pow_service_config.get("api_key", "") proxy_enabled = pow_service_config.get("proxy_enabled", False) @@ -248,9 +250,9 @@ class Database: proxy_url = proxy_url if proxy_url else None await db.execute(""" - INSERT INTO pow_service_config (id, mode, server_url, api_key, proxy_enabled, proxy_url) - VALUES (1, ?, ?, ?, ?, ?) - """, (mode, server_url, api_key, proxy_enabled, proxy_url)) + INSERT INTO pow_service_config (id, mode, use_token_for_pow, server_url, api_key, proxy_enabled, proxy_url) + VALUES (1, ?, ?, ?, ?, ?, ?) + """, (mode, use_token_for_pow, server_url, api_key, proxy_enabled, proxy_url)) async def check_and_migrate_db(self, config_dict: dict = None): @@ -319,6 +321,35 @@ class Database: except Exception as e: print(f" ✗ Failed to add column '{col_name}': {e}") + # Check and add missing columns to pow_service_config table + if await self._table_exists(db, "pow_service_config"): + added_use_token_for_pow_column = False + columns_to_add = [ + ("use_token_for_pow", "BOOLEAN DEFAULT 0"), + ] + + for col_name, col_type in columns_to_add: + if not await self._column_exists(db, "pow_service_config", col_name): + try: + await db.execute(f"ALTER TABLE pow_service_config ADD COLUMN {col_name} {col_type}") + print(f" ✓ Added column '{col_name}' to pow_service_config table") + if col_name == "use_token_for_pow": + added_use_token_for_pow_column = True + except Exception as e: + print(f" ✗ Failed to add column '{col_name}': {e}") + + # On upgrade, initialize value from setting.toml only when this column is newly added + if config_dict and added_use_token_for_pow_column: + try: + use_token_for_pow = config_dict.get("pow_service", {}).get("use_token_for_pow", False) + await db.execute(""" + UPDATE pow_service_config + SET use_token_for_pow = ? + WHERE id = 1 + """, (use_token_for_pow,)) + except Exception as e: + print(f" ✗ Failed to initialize use_token_for_pow from config: {e}") + # Check and add missing columns to watermark_free_config table if await self._table_exists(db, "watermark_free_config"): columns_to_add = [ @@ -551,6 +582,7 @@ class Database: CREATE TABLE IF NOT EXISTS pow_service_config ( id INTEGER PRIMARY KEY DEFAULT 1, mode TEXT DEFAULT 'local', + use_token_for_pow BOOLEAN DEFAULT 0, server_url TEXT, api_key TEXT, proxy_enabled BOOLEAN DEFAULT 0, @@ -1354,6 +1386,7 @@ class Database: return PowServiceConfig(**dict(row)) return PowServiceConfig( mode="local", + use_token_for_pow=False, server_url=None, api_key=None, proxy_enabled=False, @@ -1373,6 +1406,7 @@ class Database: async def update_pow_service_config( self, mode: str, + use_token_for_pow: bool = False, server_url: Optional[str] = None, api_key: Optional[str] = None, proxy_enabled: Optional[bool] = None, @@ -1382,9 +1416,9 @@ class Database: async with aiosqlite.connect(self.db_path) as db: # Use INSERT OR REPLACE to ensure the row exists await db.execute(""" - INSERT OR REPLACE INTO pow_service_config (id, mode, server_url, api_key, proxy_enabled, proxy_url, updated_at) - VALUES (1, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) - """, (mode, server_url, api_key, proxy_enabled, proxy_url)) + INSERT OR REPLACE INTO pow_service_config (id, mode, use_token_for_pow, server_url, api_key, proxy_enabled, proxy_url, updated_at) + VALUES (1, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) + """, (mode, use_token_for_pow, server_url, api_key, proxy_enabled, proxy_url)) await db.commit() diff --git a/src/core/models.py b/src/core/models.py index 49f6445..fbe0fd1 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -160,6 +160,7 @@ class PowServiceConfig(BaseModel): """POW service configuration""" id: int = 1 mode: str = "local" # "local" or "external" + use_token_for_pow: bool = False # Whether to use current token for POW calculation server_url: Optional[str] = None # External POW service URL api_key: Optional[str] = None # External POW service API key proxy_enabled: bool = False # Whether to enable proxy for POW service diff --git a/src/main.py b/src/main.py index 8a268b0..39014ff 100644 --- a/src/main.py +++ b/src/main.py @@ -147,11 +147,12 @@ async def startup_event(): # Load POW service configuration from database pow_service_config = await db.get_pow_service_config() config.set_pow_service_mode(pow_service_config.mode) + config.set_pow_service_use_token_for_pow(pow_service_config.use_token_for_pow) config.set_pow_service_server_url(pow_service_config.server_url or "") config.set_pow_service_api_key(pow_service_config.api_key or "") config.set_pow_service_proxy_enabled(pow_service_config.proxy_enabled) config.set_pow_service_proxy_url(pow_service_config.proxy_url or "") - print(f"✓ POW service mode: {pow_service_config.mode}") + print(f"✓ POW service mode: {pow_service_config.mode}, use_token_for_pow: {pow_service_config.use_token_for_pow}") # Initialize concurrency manager with all tokens all_tokens = await db.get_all_tokens() diff --git a/src/services/pow_service_client.py b/src/services/pow_service_client.py index 6d85225..e91bc4d 100644 --- a/src/services/pow_service_client.py +++ b/src/services/pow_service_client.py @@ -39,24 +39,59 @@ class POWServiceClient: headers = { "Authorization": f"Bearer {api_key}", - "Accept": "application/json" + "Accept": "application/json", + "Content-Type": "application/json", } - # Add access_token to headers if provided - if access_token: - headers["X-Access-Token"] = access_token + # Controlled by config switch: whether to pass current token to POW service + send_access_token = bool(config.pow_service_use_token_for_pow and access_token) + + def _mask_token(token_value: Optional[str]) -> str: + if not token_value: + return "none" + if len(token_value) <= 10: + return "***" + return f"{token_value[:6]}...{token_value[-4:]}" + + debug_logger.log_info( + f"[POW Service] use_token_for_pow={config.pow_service_use_token_for_pow}, access_token={_mask_token(access_token)}" + ) try: debug_logger.log_info(f"[POW Service] Requesting token from {api_url}") async with AsyncSession(impersonate="chrome131") as session: - response = await session.get( + # Preferred protocol: POST + JSON body + payload = {"flow": "sora_init"} + if send_access_token: + payload["accesstoken"] = access_token + + response = await session.post( api_url, headers=headers, + json=payload, proxy=proxy_url, timeout=30 ) + # Backward compatibility: older services may only support GET + X-Access-Token + if response.status_code in (404, 405, 415): + fallback_headers = { + "Authorization": f"Bearer {api_key}", + "Accept": "application/json" + } + if send_access_token: + fallback_headers["X-Access-Token"] = access_token + debug_logger.log_info( + f"[POW Service] POST unsupported ({response.status_code}), fallback to GET compatibility mode" + ) + response = await session.get( + api_url, + headers=fallback_headers, + proxy=proxy_url, + timeout=30 + ) + if response.status_code != 200: error_msg = f"POW service request failed: {response.status_code}" debug_logger.log_error( diff --git a/src/services/sora_client.py b/src/services/sora_client.py index 2d3b22e..2ecb626 100644 --- a/src/services/sora_client.py +++ b/src/services/sora_client.py @@ -32,7 +32,7 @@ _playwright = None _current_proxy = None # Sentinel token cache -_cached_sentinel_token = None +_cached_sentinel_token_map = {} _cached_device_id = None @@ -245,13 +245,19 @@ async def _get_cached_sentinel_token(proxy_url: str = None, force_refresh: bool Raises: Exception: If 403/429 when fetching oai-did """ - global _cached_sentinel_token + global _cached_sentinel_token_map + + # Whether current request should be token-aware for POW + use_token_for_pow = bool(config.pow_service_use_token_for_pow and access_token) + cache_key = access_token if use_token_for_pow else "__default__" # Check if external POW service is configured if config.pow_service_mode == "external": debug_logger.log_info("[POW] Using external POW service (cached sentinel)") from .pow_service_client import pow_service_client - result = await pow_service_client.get_sentinel_token(access_token=access_token) + result = await pow_service_client.get_sentinel_token( + access_token=access_token if use_token_for_pow else None + ) if result: sentinel_token, device_id, service_user_agent = result @@ -263,25 +269,36 @@ async def _get_cached_sentinel_token(proxy_url: str = None, force_refresh: bool # Local mode (original logic) # Return cached token if available and not forcing refresh - if _cached_sentinel_token and not force_refresh: - debug_logger.log_info("[Sentinel] Using cached token") - return _cached_sentinel_token + if not force_refresh and cache_key in _cached_sentinel_token_map: + if use_token_for_pow: + debug_logger.log_info("[Sentinel] Using token-scoped cached token") + else: + debug_logger.log_info("[Sentinel] Using shared cached token") + return _cached_sentinel_token_map[cache_key] # Generate new token debug_logger.log_info("[Sentinel] Generating new token...") token = await _generate_sentinel_token_lightweight(proxy_url) if token: - _cached_sentinel_token = token + _cached_sentinel_token_map[cache_key] = token debug_logger.log_info("[Sentinel] Token cached successfully") return token -def _invalidate_sentinel_cache(): - """Invalidate cached sentinel token (call after 400 error)""" - global _cached_sentinel_token - _cached_sentinel_token = None +def _invalidate_sentinel_cache(access_token: Optional[str] = None): + """Invalidate cached sentinel token (call after 400 error) + + Args: + access_token: Optional current access token for token-scoped cache invalidation + """ + global _cached_sentinel_token_map + use_token_for_pow = bool(config.pow_service_use_token_for_pow and access_token) + cache_key = access_token if use_token_for_pow else "__default__" + + if cache_key in _cached_sentinel_token_map: + del _cached_sentinel_token_map[cache_key] debug_logger.log_info("[Sentinel] Cache invalidated") @@ -755,7 +772,9 @@ class SoraClient: # Check if external POW service is configured if config.pow_service_mode == "external": debug_logger.log_info("[Sentinel] Using external POW service...") - result = await pow_service_client.get_sentinel_token(access_token=token) + result = await pow_service_client.get_sentinel_token( + access_token=token if config.pow_service_use_token_for_pow else None + ) if result: sentinel_token, device_id, service_user_agent = result @@ -1173,7 +1192,7 @@ class SoraClient: debug_logger.log_info("[Sentinel] Got 400 error, refreshing token and retrying...") # Invalidate cache and get fresh token - _invalidate_sentinel_cache() + _invalidate_sentinel_cache(token) try: sentinel_token = await _get_cached_sentinel_token(pow_proxy_url, force_refresh=True, access_token=token) diff --git a/static/manage.html b/static/manage.html index 94be007..8d063e4 100644 --- a/static/manage.html +++ b/static/manage.html @@ -402,6 +402,13 @@

选择 POW 计算方式

+
+ +

默认关闭。local 模式下使用当前轮询 Token 计算;external 模式下会传递 accesstoken 字段。

+