统一http客户端,自动 session cookie

This commit is contained in:
jingrow 2026-03-11 22:10:33 +08:00
parent 496efd8d31
commit 829174439f
12 changed files with 151 additions and 198 deletions

View File

@ -2,30 +2,30 @@
# For license information, please see license.txt # 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 import APIRouter, Request
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
import requests
import logging import logging
from jingrow.config import Config import time
from jingrow.utils.auth import get_request_session_cookie from jingrow.utils.auth import saas_get
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v2") router = APIRouter(prefix="/api/v2")
def _forward_to_saas(endpoint: str, params: dict = None) -> dict: def _forward_to_saas(endpoint: str, params: dict = None) -> dict:
"""通用转发函数""" """Forward request to SaaS using unified HTTP client"""
url = f"{Config.jingrow_server_url}/api/v2{endpoint}" start_time = time.time()
headers = {"Accept": "application/json", "Content-Type": "application/json"}
session_cookie = get_request_session_cookie() print(f"[v2] Forwarding to: {endpoint}")
if session_cookie:
headers["Cookie"] = f"sid={session_cookie}" 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: if resp.status_code == 200:
return resp.json() return resp.json()
logger.error(f"[v2] SaaS error {endpoint}: {resp.status_code}") 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") @router.get("/pagetype/{pagetype}/meta")
async def get_meta(pagetype: str): async def get_meta(pagetype: str):
"""获取 PageType 元数据 - 转发到 SaaS 端""" """Get PageType metadata - Forward to SaaS"""
result = _forward_to_saas(f"/pagetype/{pagetype}/meta") result = _forward_to_saas(f"/pagetype/{pagetype}/meta")
return JSONResponse(content=result) return JSONResponse(content=result)
@router.get("/pagetype/{pagetype}/count") @router.get("/pagetype/{pagetype}/count")
async def get_count(pagetype: str, request: Request): async def get_count(pagetype: str, request: Request):
"""获取 PageType 计数 - 转发到 SaaS 端""" """Get PageType count - Forward to SaaS"""
params = dict(request.query_params) params = dict(request.query_params)
result = _forward_to_saas(f"/pagetype/{pagetype}/count", params) result = _forward_to_saas(f"/pagetype/{pagetype}/count", params)
return JSONResponse(content=result) return JSONResponse(content=result)

View File

@ -2,29 +2,20 @@
# For license information, please see license.txt # For license information, please see license.txt
""" """
Client 相关白名单函数 - 转发到 SaaS Client whitelist functions - Forward to SaaS using unified HTTP client
""" """
import jingrow import jingrow
import requests
import logging import logging
from jingrow.config import Config from jingrow.utils.auth import saas_get, saas_post
from jingrow.utils.auth import get_request_session_cookie
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@jingrow.whitelist() @jingrow.whitelist()
def get_list(**kwargs): def get_list(**kwargs):
"""获取列表 - 转发到 SaaS 端""" """Get list - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.client.get_list" resp = saas_post('/api/action/jingrow.client.get_list', json=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", []) return resp.json().get("message", [])
logger.error(f"[get_list] SaaS error: {resp.status_code} - {resp.text[:200]}") logger.error(f"[get_list] SaaS error: {resp.status_code} - {resp.text[:200]}")
@ -33,15 +24,8 @@ def get_list(**kwargs):
@jingrow.whitelist() @jingrow.whitelist()
def get_value(**kwargs): def get_value(**kwargs):
"""获取值 - 转发到 SaaS 端""" """Get value - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.client.get_value" resp = saas_post('/api/action/jingrow.client.get_value', json=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message") return resp.json().get("message")
logger.error(f"[get_value] SaaS error: {resp.status_code} - {resp.text[:200]}") logger.error(f"[get_value] SaaS error: {resp.status_code} - {resp.text[:200]}")
@ -50,15 +34,8 @@ def get_value(**kwargs):
@jingrow.whitelist() @jingrow.whitelist()
def get_count(**kwargs): def get_count(**kwargs):
"""获取计数 - 转发到 SaaS 端""" """Get count - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.client.get_count" resp = saas_get('/api/action/jingrow.client.get_count', params=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", 0) return resp.json().get("message", 0)
logger.error(f"[get_count] SaaS error: {resp.status_code} - {resp.text[:200]}") logger.error(f"[get_count] SaaS error: {resp.status_code} - {resp.text[:200]}")

View File

@ -2,29 +2,20 @@
# For license information, please see license.txt # For license information, please see license.txt
""" """
User 相关白名单函数 - 转发到 SaaS User whitelist functions - Forward to SaaS using unified HTTP client
""" """
import jingrow import jingrow
import requests
import logging import logging
from jingrow.config import Config from jingrow.utils.auth import saas_post
from jingrow.utils.auth import get_request_session_cookie
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@jingrow.whitelist() @jingrow.whitelist()
def get_all_roles(**kwargs): def get_all_roles(**kwargs):
"""获取所有角色 - 转发到 SaaS 端""" """Get all roles - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.core.pagetype.user.user.get_all_roles" resp = saas_post('/api/action/jingrow.core.pagetype.user.user.get_all_roles', json=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", []) return resp.json().get("message", [])
logger.error(f"[user.get_all_roles] SaaS error: {resp.status_code}") logger.error(f"[user.get_all_roles] SaaS error: {resp.status_code}")

View File

@ -2,29 +2,20 @@
# For license information, please see license.txt # For license information, please see license.txt
""" """
Desktop 相关白名单函数 - 转发到 SaaS Desktop whitelist functions - Forward to SaaS using unified HTTP client
""" """
import jingrow import jingrow
import requests
import logging import logging
from jingrow.config import Config from jingrow.utils.auth import saas_post
from jingrow.utils.auth import get_request_session_cookie
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@jingrow.whitelist() @jingrow.whitelist()
def get_workspace_sidebar_items(**kwargs): def get_workspace_sidebar_items(**kwargs):
"""获取工作区侧边栏项目 - 转发到 SaaS 端""" """Get workspace sidebar items - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.desktop.get_workspace_sidebar_items" resp = saas_post('/api/action/jingrow.desk.desktop.get_workspace_sidebar_items', json=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", {}) return resp.json().get("message", {})
logger.error(f"[get_workspace_sidebar_items] SaaS error: {resp.status_code}") logger.error(f"[get_workspace_sidebar_items] SaaS error: {resp.status_code}")
@ -33,15 +24,8 @@ def get_workspace_sidebar_items(**kwargs):
@jingrow.whitelist() @jingrow.whitelist()
def get_desktop_page(**kwargs): def get_desktop_page(**kwargs):
"""获取桌面页面 - 转发到 SaaS 端""" """Get desktop page - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.desktop.get_desktop_page" resp = saas_post('/api/action/jingrow.desk.desktop.get_desktop_page', json=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", {}) return resp.json().get("message", {})
logger.error(f"[get_desktop_page] SaaS error: {resp.status_code}") logger.error(f"[get_desktop_page] SaaS error: {resp.status_code}")

View File

@ -2,32 +2,33 @@
# For license information, please see license.txt # For license information, please see license.txt
""" """
Form load 相关白名单函数 - 转发到 SaaS Form load whitelist functions - Forward to SaaS using unified HTTP client
""" """
import jingrow import jingrow
import requests
import logging import logging
from jingrow.config import Config import time
from jingrow.utils.auth import get_request_session_cookie from jingrow.utils.auth import saas_post
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@jingrow.whitelist() @jingrow.whitelist()
def getdoc(**kwargs): def getdoc(**kwargs):
"""获取文档 - 转发到 SaaS 端""" """Get document - Forward to SaaS using unified HTTP client"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.form.load.getdoc" start_time = time.time()
headers = {"Accept": "application/json", "Content-Type": "application/json"}
session_cookie = get_request_session_cookie() print(f"[getdoc] Requesting: pagetype={kwargs.get('pagetype')}, name={kwargs.get('name')}")
if session_cookie:
headers["Cookie"] = f"sid={session_cookie}" 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: if resp.status_code == 200:
# SaaS getdoc 直接返回 {docs: [...], docinfo: {...}} 格式 result = resp.json()
# 不包装在 message 里 resp_size = len(resp.content) / 1024 # KB
return resp.json() print(f"[getdoc] Total time: {time.time() - start_time:.3f}s, response size: {resp_size:.1f}KB")
logger.error(f"[getdoc] SaaS error: {resp.status_code} - {resp.text[:200]}") return result
print(f"[getdoc] SaaS error: {resp.status_code} - {resp.text[:200]}")
return {} return {}

View File

@ -2,29 +2,20 @@
# For license information, please see license.txt # For license information, please see license.txt
""" """
Dashboard Chart 相关白名单函数 - 转发到 SaaS Dashboard Chart whitelist functions - Forward to SaaS using unified HTTP client
""" """
import jingrow import jingrow
import requests
import logging import logging
from jingrow.config import Config from jingrow.utils.auth import saas_post
from jingrow.utils.auth import get_request_session_cookie
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@jingrow.whitelist() @jingrow.whitelist()
def get(**kwargs): def get(**kwargs):
"""获取 Dashboard Chart - 转发到 SaaS 端""" """Get Dashboard Chart - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.pagetype.dashboard_chart.dashboard_chart.get" resp = saas_post('/api/action/jingrow.desk.pagetype.dashboard_chart.dashboard_chart.get', json=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", {}) return resp.json().get("message", {})
logger.error(f"[dashboard_chart.get] SaaS error: {resp.status_code}") logger.error(f"[dashboard_chart.get] SaaS error: {resp.status_code}")

View File

@ -2,29 +2,20 @@
# For license information, please see license.txt # 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 jingrow
import requests
import logging import logging
from jingrow.config import Config from jingrow.utils.auth import saas_post
from jingrow.utils.auth import get_request_session_cookie
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@jingrow.whitelist() @jingrow.whitelist()
def get_config(**kwargs): def get_config(**kwargs):
"""获取 Chart Source 配置 - 转发到 SaaS 端""" """Get Chart Source config - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.pagetype.dashboard_chart_source.dashboard_chart_source.get_config" resp = saas_post('/api/action/jingrow.desk.pagetype.dashboard_chart_source.dashboard_chart_source.get_config', json=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", {}) return resp.json().get("message", {})
logger.error(f"[dashboard_chart_source.get_config] SaaS error: {resp.status_code}") logger.error(f"[dashboard_chart_source.get_config] SaaS error: {resp.status_code}")

View File

@ -2,29 +2,20 @@
# For license information, please see license.txt # For license information, please see license.txt
""" """
Query Report 相关白名单函数 - 转发到 SaaS Query Report whitelist functions - Forward to SaaS using unified HTTP client
""" """
import jingrow import jingrow
import requests
import logging import logging
from jingrow.config import Config from jingrow.utils.auth import saas_post
from jingrow.utils.auth import get_request_session_cookie
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@jingrow.whitelist() @jingrow.whitelist()
def run(**kwargs): def run(**kwargs):
"""运行查询报表 - 转发到 SaaS 端""" """Run query report - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.query_report.run" resp = saas_post('/api/action/jingrow.desk.query_report.run', json=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", {}) return resp.json().get("message", {})
logger.error(f"[query_report.run] SaaS error: {resp.status_code}") logger.error(f"[query_report.run] SaaS error: {resp.status_code}")
@ -33,15 +24,8 @@ def run(**kwargs):
@jingrow.whitelist() @jingrow.whitelist()
def get_script(**kwargs): def get_script(**kwargs):
"""获取报表脚本 - 转发到 SaaS 端""" """Get report script - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.query_report.get_script" resp = saas_post('/api/action/jingrow.desk.query_report.get_script', json=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", {}) return resp.json().get("message", {})
logger.error(f"[query_report.get_script] SaaS error: {resp.status_code}") logger.error(f"[query_report.get_script] SaaS error: {resp.status_code}")

View File

@ -2,29 +2,20 @@
# For license information, please see license.txt # For license information, please see license.txt
""" """
Search 相关白名单函数 - 转发到 SaaS Search whitelist functions - Forward to SaaS using unified HTTP client
""" """
import jingrow import jingrow
import requests
import logging import logging
from jingrow.config import Config from jingrow.utils.auth import saas_post
from jingrow.utils.auth import get_request_session_cookie
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@jingrow.whitelist() @jingrow.whitelist()
def search_link(**kwargs): def search_link(**kwargs):
"""搜索链接 - 转发到 SaaS 端""" """Search link - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.desk.search.search_link" resp = saas_post('/api/action/jingrow.desk.search.search_link', json=kwargs)
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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", []) return resp.json().get("message", [])
logger.error(f"[search_link] SaaS error: {resp.status_code}") logger.error(f"[search_link] SaaS error: {resp.status_code}")

View File

@ -2,7 +2,7 @@
# For license information, please see license.txt # For license information, please see license.txt
""" """
Jingrow 白名单路由服务 Jingrow whitelist routing service
""" """
from fastapi import APIRouter, Request, HTTPException from fastapi import APIRouter, Request, HTTPException
@ -11,7 +11,7 @@ import importlib
from typing import Any, Dict from typing import Any, Dict
import logging import logging
from jingrow import get_whitelisted_function 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.jingrow_api import get_logged_user
from jingrow.utils.app_manager import ensure_apps_on_sys_path 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: async def authenticate_request(request: Request, allow_guest: bool) -> bool:
""" """
认证请求支持两种认证方式 Authenticate request. Supports:
1. Session Cookie 认证 1. Session Cookie authentication
2. API Key 认证 2. API Key authentication
""" """
if allow_guest: if allow_guest:
return True return True
# 方式1: 检查 Session Cookie 认证 # Method 1: Check Session Cookie authentication
session_cookie = request.cookies.get('sid') session_cookie = request.cookies.get('sid')
if session_cookie: if session_cookie:
try: try:
user = get_logged_user(session_cookie) user = get_logged_user(session_cookie)
if user: 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 return True
except Exception as e: 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') auth_header = request.headers.get('Authorization')
if auth_header and auth_header.startswith('token '): if auth_header and auth_header.startswith('token '):
try: try:
# 验证API Key格式: token key:secret token_part = auth_header[6:]
token_part = auth_header[6:] # 移除 "token " 前缀
if ':' in token_part: if ':' in token_part:
api_key, api_secret = token_part.split(':', 1) api_key, api_secret = token_part.split(':', 1)
# 验证API Key是否有效
expected_headers = get_jingrow_api_headers() expected_headers = get_jingrow_api_headers()
if expected_headers and expected_headers.get('Authorization') == auth_header: if expected_headers and expected_headers.get('Authorization') == auth_header:
logger.info("API Key认证成功") logger.info("API Key authenticated")
return True return True
except Exception as e: 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 return False
async def _process_whitelist_call(request: Request, full_module_path: str): async def _process_whitelist_call(request: Request, full_module_path: str):

View File

@ -2,26 +2,17 @@
# For license information, please see license.txt # For license information, please see license.txt
""" """
会话相关白名单函数 - 转发到 SaaS Session whitelist functions - Forward to SaaS using unified HTTP client
""" """
import jingrow import jingrow
import requests from jingrow.utils.auth import saas_get
from jingrow.config import Config
from jingrow.utils.auth import get_request_session_cookie
@jingrow.whitelist() @jingrow.whitelist()
def get_boot_info(): def get_boot_info():
"""获取启动信息 - 转发到 SaaS 端""" """Get boot info - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.sessions.get_boot_info" resp = saas_get('/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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", {}) return resp.json().get("message", {})
return {} return {}
@ -29,15 +20,8 @@ def get_boot_info():
@jingrow.whitelist() @jingrow.whitelist()
def get_translations(): def get_translations():
"""获取翻译 - 转发到 SaaS 端""" """Get translations - Forward to SaaS"""
url = f"{Config.jingrow_server_url}/api/action/jingrow.sessions.get_translations" resp = saas_get('/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)
if resp.status_code == 200: if resp.status_code == 200:
return resp.json().get("message", {}) return resp.json().get("message", {})
return {} return {}

View File

@ -2,10 +2,10 @@ import threading
import sys import sys
import os import os
import requests import requests
from typing import Optional from typing import Optional, Dict, Any
from contextvars import ContextVar 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__))))))) 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 from jingrow.config import Config
@ -14,20 +14,79 @@ _thread_local = threading.local()
# ContextVar for async-safe session cookie storage # ContextVar for async-safe session cookie storage
_request_session_cookie: ContextVar[Optional[str]] = ContextVar('request_session_cookie', default=None) _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]): def set_request_session_cookie(cookie: Optional[str]):
"""设置当前请求的 session cookie异步安全""" """Set current request's session cookie (async-safe)"""
_request_session_cookie.set(cookie) _request_session_cookie.set(cookie)
def get_request_session_cookie() -> Optional[str]: def get_request_session_cookie() -> Optional[str]:
"""获取当前请求的 session cookie异步安全""" """Get current request's session cookie (async-safe)"""
return _request_session_cookie.get() return _request_session_cookie.get()
def set_context(context): def set_context(context):
"""设置当前线程的context""" """Set current thread's context"""
_thread_local.context = context _thread_local.context = context
def get_jingrow_api_headers(): def get_jingrow_api_headers():
"""获取 Jingrow API 认证头部,用于系统级调用""" """Get Jingrow API auth headers for system-level calls"""
api_key = Config.jingrow_api_key api_key = Config.jingrow_api_key
api_secret = Config.jingrow_api_secret api_secret = Config.jingrow_api_secret