diff --git a/apps/jingrow/jingrow/api/v2.py b/apps/jingrow/jingrow/api/v2.py index 985d5dc..886c15b 100644 --- a/apps/jingrow/jingrow/api/v2.py +++ b/apps/jingrow/jingrow/api/v2.py @@ -2,30 +2,30 @@ # For license information, please see license.txt """ -API v2 路由 - 转发到 SaaS 端 +API v2 routes - Forward to SaaS using unified HTTP client """ from fastapi import APIRouter, Request from fastapi.responses import JSONResponse -import requests import logging -from jingrow.config import Config -from jingrow.utils.auth import get_request_session_cookie +import time +from jingrow.utils.auth import saas_get logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/v2") def _forward_to_saas(endpoint: str, params: dict = None) -> dict: - """通用转发函数""" - url = f"{Config.jingrow_server_url}/api/v2{endpoint}" - headers = {"Accept": "application/json", "Content-Type": "application/json"} + """Forward request to SaaS using unified HTTP client""" + start_time = time.time() - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" + print(f"[v2] Forwarding to: {endpoint}") + + resp = saas_get(f"/api/v2{endpoint}", params=params) + + req_time = time.time() - start_time + print(f"[v2] Response: status={resp.status_code}, time={req_time:.3f}s, endpoint={endpoint}") - resp = requests.get(url, headers=headers, params=params, timeout=30) if resp.status_code == 200: return resp.json() logger.error(f"[v2] SaaS error {endpoint}: {resp.status_code}") @@ -34,14 +34,14 @@ def _forward_to_saas(endpoint: str, params: dict = None) -> dict: @router.get("/pagetype/{pagetype}/meta") async def get_meta(pagetype: str): - """获取 PageType 元数据 - 转发到 SaaS 端""" + """Get PageType metadata - Forward to SaaS""" result = _forward_to_saas(f"/pagetype/{pagetype}/meta") return JSONResponse(content=result) @router.get("/pagetype/{pagetype}/count") async def get_count(pagetype: str, request: Request): - """获取 PageType 计数 - 转发到 SaaS 端""" + """Get PageType count - Forward to SaaS""" params = dict(request.query_params) result = _forward_to_saas(f"/pagetype/{pagetype}/count", params) return JSONResponse(content=result) diff --git a/apps/jingrow/jingrow/client.py b/apps/jingrow/jingrow/client.py index e03a0f1..79a8a47 100644 --- a/apps/jingrow/jingrow/client.py +++ b/apps/jingrow/jingrow/client.py @@ -2,29 +2,20 @@ # For license information, please see license.txt """ -Client 相关白名单函数 - 转发到 SaaS 端 +Client whitelist functions - Forward to SaaS using unified HTTP client """ import jingrow -import requests import logging -from jingrow.config import Config -from jingrow.utils.auth import get_request_session_cookie +from jingrow.utils.auth import saas_get, saas_post logger = logging.getLogger(__name__) @jingrow.whitelist() def get_list(**kwargs): - """获取列表 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.client.get_list" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) + """Get list - Forward to SaaS""" + resp = saas_post('/api/action/jingrow.client.get_list', json=kwargs) if resp.status_code == 200: return resp.json().get("message", []) logger.error(f"[get_list] SaaS error: {resp.status_code} - {resp.text[:200]}") @@ -33,15 +24,8 @@ def get_list(**kwargs): @jingrow.whitelist() def get_value(**kwargs): - """获取值 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.client.get_value" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) + """Get value - Forward to SaaS""" + resp = saas_post('/api/action/jingrow.client.get_value', json=kwargs) if resp.status_code == 200: return resp.json().get("message") logger.error(f"[get_value] SaaS error: {resp.status_code} - {resp.text[:200]}") @@ -50,15 +34,8 @@ def get_value(**kwargs): @jingrow.whitelist() def get_count(**kwargs): - """获取计数 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.client.get_count" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.get(url, headers=headers, params=kwargs, timeout=30) + """Get count - Forward to SaaS""" + resp = saas_get('/api/action/jingrow.client.get_count', params=kwargs) if resp.status_code == 200: return resp.json().get("message", 0) logger.error(f"[get_count] SaaS error: {resp.status_code} - {resp.text[:200]}") diff --git a/apps/jingrow/jingrow/core/pagetype/user/user.py b/apps/jingrow/jingrow/core/pagetype/user/user.py index 9a30c5f..3184fbf 100644 --- a/apps/jingrow/jingrow/core/pagetype/user/user.py +++ b/apps/jingrow/jingrow/core/pagetype/user/user.py @@ -2,29 +2,20 @@ # For license information, please see license.txt """ -User 相关白名单函数 - 转发到 SaaS 端 +User whitelist functions - Forward to SaaS using unified HTTP client """ import jingrow -import requests import logging -from jingrow.config import Config -from jingrow.utils.auth import get_request_session_cookie +from jingrow.utils.auth import saas_post logger = logging.getLogger(__name__) @jingrow.whitelist() def get_all_roles(**kwargs): - """获取所有角色 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.core.pagetype.user.user.get_all_roles" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) + """Get all roles - Forward to SaaS""" + resp = saas_post('/api/action/jingrow.core.pagetype.user.user.get_all_roles', json=kwargs) if resp.status_code == 200: return resp.json().get("message", []) logger.error(f"[user.get_all_roles] SaaS error: {resp.status_code}") diff --git a/apps/jingrow/jingrow/desk/desktop.py b/apps/jingrow/jingrow/desk/desktop.py index efd3115..17769d5 100644 --- a/apps/jingrow/jingrow/desk/desktop.py +++ b/apps/jingrow/jingrow/desk/desktop.py @@ -2,29 +2,20 @@ # For license information, please see license.txt """ -Desktop 相关白名单函数 - 转发到 SaaS 端 +Desktop whitelist functions - Forward to SaaS using unified HTTP client """ import jingrow -import requests import logging -from jingrow.config import Config -from jingrow.utils.auth import get_request_session_cookie +from jingrow.utils.auth import saas_post logger = logging.getLogger(__name__) @jingrow.whitelist() def get_workspace_sidebar_items(**kwargs): - """获取工作区侧边栏项目 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.desktop.get_workspace_sidebar_items" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) + """Get workspace sidebar items - Forward to SaaS""" + resp = saas_post('/api/action/jingrow.desk.desktop.get_workspace_sidebar_items', json=kwargs) if resp.status_code == 200: return resp.json().get("message", {}) logger.error(f"[get_workspace_sidebar_items] SaaS error: {resp.status_code}") @@ -33,15 +24,8 @@ def get_workspace_sidebar_items(**kwargs): @jingrow.whitelist() def get_desktop_page(**kwargs): - """获取桌面页面 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.desktop.get_desktop_page" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) + """Get desktop page - Forward to SaaS""" + resp = saas_post('/api/action/jingrow.desk.desktop.get_desktop_page', json=kwargs) if resp.status_code == 200: return resp.json().get("message", {}) logger.error(f"[get_desktop_page] SaaS error: {resp.status_code}") diff --git a/apps/jingrow/jingrow/desk/form/load.py b/apps/jingrow/jingrow/desk/form/load.py index 789a9d9..8466ea7 100644 --- a/apps/jingrow/jingrow/desk/form/load.py +++ b/apps/jingrow/jingrow/desk/form/load.py @@ -2,32 +2,33 @@ # For license information, please see license.txt """ -Form load 相关白名单函数 - 转发到 SaaS 端 +Form load whitelist functions - Forward to SaaS using unified HTTP client """ import jingrow -import requests import logging -from jingrow.config import Config -from jingrow.utils.auth import get_request_session_cookie +import time +from jingrow.utils.auth import saas_post logger = logging.getLogger(__name__) @jingrow.whitelist() def getdoc(**kwargs): - """获取文档 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.form.load.getdoc" - headers = {"Accept": "application/json", "Content-Type": "application/json"} + """Get document - Forward to SaaS using unified HTTP client""" + start_time = time.time() - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" + print(f"[getdoc] Requesting: pagetype={kwargs.get('pagetype')}, name={kwargs.get('name')}") + + resp = saas_post('/api/action/jingrow.desk.form.load.getdoc', json=kwargs) + + req_time = time.time() - start_time + print(f"[getdoc] SaaS response: status={resp.status_code}, time={req_time:.3f}s") - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) if resp.status_code == 200: - # SaaS getdoc 直接返回 {docs: [...], docinfo: {...}} 格式 - # 不包装在 message 里 - return resp.json() - logger.error(f"[getdoc] SaaS error: {resp.status_code} - {resp.text[:200]}") + result = resp.json() + resp_size = len(resp.content) / 1024 # KB + print(f"[getdoc] Total time: {time.time() - start_time:.3f}s, response size: {resp_size:.1f}KB") + return result + print(f"[getdoc] SaaS error: {resp.status_code} - {resp.text[:200]}") return {} diff --git a/apps/jingrow/jingrow/desk/pagetype/dashboard_chart/dashboard_chart.py b/apps/jingrow/jingrow/desk/pagetype/dashboard_chart/dashboard_chart.py index 443294a..9130b24 100644 --- a/apps/jingrow/jingrow/desk/pagetype/dashboard_chart/dashboard_chart.py +++ b/apps/jingrow/jingrow/desk/pagetype/dashboard_chart/dashboard_chart.py @@ -2,29 +2,20 @@ # For license information, please see license.txt """ -Dashboard Chart 相关白名单函数 - 转发到 SaaS 端 +Dashboard Chart whitelist functions - Forward to SaaS using unified HTTP client """ import jingrow -import requests import logging -from jingrow.config import Config -from jingrow.utils.auth import get_request_session_cookie +from jingrow.utils.auth import saas_post logger = logging.getLogger(__name__) @jingrow.whitelist() def get(**kwargs): - """获取 Dashboard Chart - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.pagetype.dashboard_chart.dashboard_chart.get" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) + """Get Dashboard Chart - Forward to SaaS""" + resp = saas_post('/api/action/jingrow.desk.pagetype.dashboard_chart.dashboard_chart.get', json=kwargs) if resp.status_code == 200: return resp.json().get("message", {}) logger.error(f"[dashboard_chart.get] SaaS error: {resp.status_code}") diff --git a/apps/jingrow/jingrow/desk/pagetype/dashboard_chart_source/dashboard_chart_source.py b/apps/jingrow/jingrow/desk/pagetype/dashboard_chart_source/dashboard_chart_source.py index fd2b0a2..3efdc67 100644 --- a/apps/jingrow/jingrow/desk/pagetype/dashboard_chart_source/dashboard_chart_source.py +++ b/apps/jingrow/jingrow/desk/pagetype/dashboard_chart_source/dashboard_chart_source.py @@ -2,29 +2,20 @@ # For license information, please see license.txt """ -Dashboard Chart Source 相关白名单函数 - 转发到 SaaS 端 +Dashboard Chart Source whitelist functions - Forward to SaaS using unified HTTP client """ import jingrow -import requests import logging -from jingrow.config import Config -from jingrow.utils.auth import get_request_session_cookie +from jingrow.utils.auth import saas_post logger = logging.getLogger(__name__) @jingrow.whitelist() def get_config(**kwargs): - """获取 Chart Source 配置 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.pagetype.dashboard_chart_source.dashboard_chart_source.get_config" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) + """Get Chart Source config - Forward to SaaS""" + resp = saas_post('/api/action/jingrow.desk.pagetype.dashboard_chart_source.dashboard_chart_source.get_config', json=kwargs) if resp.status_code == 200: return resp.json().get("message", {}) logger.error(f"[dashboard_chart_source.get_config] SaaS error: {resp.status_code}") diff --git a/apps/jingrow/jingrow/desk/query_report.py b/apps/jingrow/jingrow/desk/query_report.py index b12f801..b0e4c09 100644 --- a/apps/jingrow/jingrow/desk/query_report.py +++ b/apps/jingrow/jingrow/desk/query_report.py @@ -2,29 +2,20 @@ # For license information, please see license.txt """ -Query Report 相关白名单函数 - 转发到 SaaS 端 +Query Report whitelist functions - Forward to SaaS using unified HTTP client """ import jingrow -import requests import logging -from jingrow.config import Config -from jingrow.utils.auth import get_request_session_cookie +from jingrow.utils.auth import saas_post logger = logging.getLogger(__name__) @jingrow.whitelist() def run(**kwargs): - """运行查询报表 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.query_report.run" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) + """Run query report - Forward to SaaS""" + resp = saas_post('/api/action/jingrow.desk.query_report.run', json=kwargs) if resp.status_code == 200: return resp.json().get("message", {}) logger.error(f"[query_report.run] SaaS error: {resp.status_code}") @@ -33,15 +24,8 @@ def run(**kwargs): @jingrow.whitelist() def get_script(**kwargs): - """获取报表脚本 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.query_report.get_script" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) + """Get report script - Forward to SaaS""" + resp = saas_post('/api/action/jingrow.desk.query_report.get_script', json=kwargs) if resp.status_code == 200: return resp.json().get("message", {}) logger.error(f"[query_report.get_script] SaaS error: {resp.status_code}") diff --git a/apps/jingrow/jingrow/desk/search/search.py b/apps/jingrow/jingrow/desk/search/search.py index e930d6e..99e0c41 100644 --- a/apps/jingrow/jingrow/desk/search/search.py +++ b/apps/jingrow/jingrow/desk/search/search.py @@ -2,29 +2,20 @@ # For license information, please see license.txt """ -Search 相关白名单函数 - 转发到 SaaS 端 +Search whitelist functions - Forward to SaaS using unified HTTP client """ import jingrow -import requests import logging -from jingrow.config import Config -from jingrow.utils.auth import get_request_session_cookie +from jingrow.utils.auth import saas_post logger = logging.getLogger(__name__) @jingrow.whitelist() def search_link(**kwargs): - """搜索链接 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.search.search_link" - headers = {"Accept": "application/json", "Content-Type": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.post(url, headers=headers, json=kwargs, timeout=30) + """Search link - Forward to SaaS""" + resp = saas_post('/api/action/jingrow.desk.search.search_link', json=kwargs) if resp.status_code == 200: return resp.json().get("message", []) logger.error(f"[search_link] SaaS error: {resp.status_code}") diff --git a/apps/jingrow/jingrow/services/whitelist.py b/apps/jingrow/jingrow/services/whitelist.py index e6b4354..b6ae59a 100644 --- a/apps/jingrow/jingrow/services/whitelist.py +++ b/apps/jingrow/jingrow/services/whitelist.py @@ -2,7 +2,7 @@ # For license information, please see license.txt """ -Jingrow 白名单路由服务 +Jingrow whitelist routing service """ from fastapi import APIRouter, Request, HTTPException @@ -11,7 +11,7 @@ import importlib from typing import Any, Dict import logging from jingrow import get_whitelisted_function -from jingrow.utils.auth import get_jingrow_api_headers +from jingrow.utils.auth import get_jingrow_api_headers, set_request_session_cookie from jingrow.utils.jingrow_api import get_logged_user from jingrow.utils.app_manager import ensure_apps_on_sys_path @@ -20,41 +20,41 @@ router = APIRouter(prefix="/api/action") async def authenticate_request(request: Request, allow_guest: bool) -> bool: """ - 认证请求,支持两种认证方式: - 1. Session Cookie 认证 - 2. API Key 认证 + Authenticate request. Supports: + 1. Session Cookie authentication + 2. API Key authentication """ if allow_guest: return True - # 方式1: 检查 Session Cookie 认证 + # Method 1: Check Session Cookie authentication session_cookie = request.cookies.get('sid') if session_cookie: try: user = get_logged_user(session_cookie) if user: - logger.info(f"Session认证成功: {user}") + # Store session cookie in ContextVar for downstream use + set_request_session_cookie(session_cookie) + logger.info(f"Session authenticated: {user}") return True except Exception as e: - logger.warning(f"Session认证失败: {e}") + logger.warning(f"Session authentication failed: {e}") - # 方式2: 检查 API Key 认证 + # Method 2: Check API Key authentication auth_header = request.headers.get('Authorization') if auth_header and auth_header.startswith('token '): try: - # 验证API Key格式: token key:secret - token_part = auth_header[6:] # 移除 "token " 前缀 + token_part = auth_header[6:] if ':' in token_part: api_key, api_secret = token_part.split(':', 1) - # 验证API Key是否有效 expected_headers = get_jingrow_api_headers() if expected_headers and expected_headers.get('Authorization') == auth_header: - logger.info("API Key认证成功") + logger.info("API Key authenticated") return True except Exception as e: - logger.warning(f"API Key认证失败: {e}") + logger.warning(f"API Key authentication failed: {e}") - logger.warning("认证失败: 未提供有效的认证信息") + logger.warning("Authentication failed: no valid credentials") return False async def _process_whitelist_call(request: Request, full_module_path: str): diff --git a/apps/jingrow/jingrow/sessions.py b/apps/jingrow/jingrow/sessions.py index 14bf1ad..8237c9e 100644 --- a/apps/jingrow/jingrow/sessions.py +++ b/apps/jingrow/jingrow/sessions.py @@ -2,26 +2,17 @@ # For license information, please see license.txt """ -会话相关白名单函数 - 转发到 SaaS 端 +Session whitelist functions - Forward to SaaS using unified HTTP client """ import jingrow -import requests -from jingrow.config import Config -from jingrow.utils.auth import get_request_session_cookie +from jingrow.utils.auth import saas_get @jingrow.whitelist() def get_boot_info(): - """获取启动信息 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.sessions.get_boot_info" - headers = {"Accept": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.get(url, headers=headers, timeout=30) + """Get boot info - Forward to SaaS""" + resp = saas_get('/api/action/jingrow.sessions.get_boot_info') if resp.status_code == 200: return resp.json().get("message", {}) return {} @@ -29,15 +20,8 @@ def get_boot_info(): @jingrow.whitelist() def get_translations(): - """获取翻译 - 转发到 SaaS 端""" - url = f"{Config.jingrow_server_url}/api/action/jingrow.sessions.get_translations" - headers = {"Accept": "application/json"} - - session_cookie = get_request_session_cookie() - if session_cookie: - headers["Cookie"] = f"sid={session_cookie}" - - resp = requests.get(url, headers=headers, timeout=30) + """Get translations - Forward to SaaS""" + resp = saas_get('/api/action/jingrow.sessions.get_translations') if resp.status_code == 200: return resp.json().get("message", {}) return {} diff --git a/apps/jingrow/jingrow/utils/auth.py b/apps/jingrow/jingrow/utils/auth.py index f407e5a..9cd0f18 100644 --- a/apps/jingrow/jingrow/utils/auth.py +++ b/apps/jingrow/jingrow/utils/auth.py @@ -2,10 +2,10 @@ import threading import sys import os import requests -from typing import Optional +from typing import Optional, Dict, Any from contextvars import ContextVar -# 导入配置 +# Import config sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))) from jingrow.config import Config @@ -14,20 +14,79 @@ _thread_local = threading.local() # ContextVar for async-safe session cookie storage _request_session_cookie: ContextVar[Optional[str]] = ContextVar('request_session_cookie', default=None) +# Global HTTP session with connection pooling +_http_session: Optional[requests.Session] = None + +def _get_http_session() -> requests.Session: + """Get or create global HTTP session with connection pooling""" + global _http_session + if _http_session is None: + _http_session = requests.Session() + adapter = requests.adapters.HTTPAdapter( + pool_connections=10, + pool_maxsize=20, + max_retries=2 + ) + _http_session.mount('http://', adapter) + _http_session.mount('https://', adapter) + return _http_session + +def saas_request(method: str, endpoint: str, **kwargs) -> requests.Response: + """ + Unified HTTP client for SaaS requests with connection pooling and auto session cookie. + + Args: + method: HTTP method (GET, POST, etc.) + endpoint: API endpoint (e.g., '/api/action/jingrow.xxx' or '/api/v2/pagetype/User/meta') + **kwargs: Additional arguments for requests (json, params, data, timeout, etc.) + + Returns: + requests.Response + + Example: + # GET request + resp = saas_request('GET', '/api/v2/pagetype/User/meta') + + # POST request + resp = saas_request('POST', '/api/action/jingrow.desk.form.load.getdoc', json={'pagetype': 'User', 'name': 'Administrator'}) + """ + url = f"{Config.jingrow_server_url}{endpoint}" + headers = kwargs.pop('headers', {}) + headers.setdefault('Accept', 'application/json') + headers.setdefault('Content-Type', 'application/json') + + # Auto-add session cookie from ContextVar + session_cookie = get_request_session_cookie() + if session_cookie: + headers['Cookie'] = f"sid={session_cookie}" + + timeout = kwargs.pop('timeout', 30) + + session = _get_http_session() + return session.request(method.upper(), url, headers=headers, timeout=timeout, **kwargs) + +def saas_get(endpoint: str, **kwargs) -> requests.Response: + """Convenience method for GET requests to SaaS""" + return saas_request('GET', endpoint, **kwargs) + +def saas_post(endpoint: str, **kwargs) -> requests.Response: + """Convenience method for POST requests to SaaS""" + return saas_request('POST', endpoint, **kwargs) + def set_request_session_cookie(cookie: Optional[str]): - """设置当前请求的 session cookie(异步安全)""" + """Set current request's session cookie (async-safe)""" _request_session_cookie.set(cookie) def get_request_session_cookie() -> Optional[str]: - """获取当前请求的 session cookie(异步安全)""" + """Get current request's session cookie (async-safe)""" return _request_session_cookie.get() def set_context(context): - """设置当前线程的context""" + """Set current thread's context""" _thread_local.context = context def get_jingrow_api_headers(): - """获取 Jingrow API 认证头部,用于系统级调用""" + """Get Jingrow API auth headers for system-level calls""" api_key = Config.jingrow_api_key api_secret = Config.jingrow_api_secret