94 lines
3.4 KiB
Python
94 lines
3.4 KiB
Python
"""ReinLoop cloud-server endpoint configuration shared by core modules."""
|
|
|
|
import os
|
|
import threading
|
|
import time
|
|
|
|
import requests
|
|
|
|
from license_utils import get_verified_license
|
|
|
|
|
|
base_url = os.environ.get(
|
|
"REINLOOP_SERVER_URL",
|
|
"https://ReinLoop.dominatedconvergence.com",
|
|
).rstrip("/")
|
|
server_api_url = os.environ.get(
|
|
"REINLOOP_API_URL",
|
|
base_url,
|
|
)
|
|
# Compatibility alias used by existing modules. It points to the ReinLoop
|
|
# Express server API, not a cloud-function endpoint.
|
|
data_record_url = server_api_url
|
|
device_api_url = f"{server_api_url.rstrip('/')}/device"
|
|
_license = get_verified_license()
|
|
_license_device_id = (_license or {}).get("device_id", "").strip()
|
|
_environment_device_id = os.environ.get("REINLOOP_DEVICE_ID", "").strip()
|
|
|
|
if _license_device_id and _environment_device_id and _license_device_id != _environment_device_id:
|
|
raise RuntimeError("REINLOOP_DEVICE_ID 与许可证 device_id 不一致")
|
|
|
|
the_folder = _license_device_id or _environment_device_id or "local-test-device"
|
|
|
|
if not the_folder:
|
|
raise RuntimeError("设备 ID 不能为空")
|
|
|
|
_DEVICE_TOKEN = None
|
|
_DEVICE_TOKEN_EXPIRES_AT_MS = 0
|
|
_DEVICE_TOKEN_LOCK = threading.Lock()
|
|
|
|
|
|
def _current_license_context():
|
|
payload = get_verified_license() or {}
|
|
license_id = str(payload.get("license_id") or "").strip()
|
|
device_id = str(payload.get("device_id") or _environment_device_id or the_folder).strip()
|
|
return license_id, device_id
|
|
|
|
|
|
def _ensure_device_token(timeout=10):
|
|
global _DEVICE_TOKEN, _DEVICE_TOKEN_EXPIRES_AT_MS
|
|
with _DEVICE_TOKEN_LOCK:
|
|
now_ms = int(time.time() * 1000)
|
|
if _DEVICE_TOKEN and _DEVICE_TOKEN_EXPIRES_AT_MS - now_ms > 30_000:
|
|
return _DEVICE_TOKEN
|
|
|
|
license_id, device_id = _current_license_context()
|
|
if not license_id:
|
|
raise RuntimeError("许可证未就绪,无法获取 deviceToken")
|
|
|
|
response = requests.post(device_api_url, json={
|
|
"type": "deviceAuth",
|
|
"licenseId": license_id,
|
|
"deviceId": device_id,
|
|
}, timeout=timeout)
|
|
response.raise_for_status()
|
|
result = response.json()
|
|
if not result.get("success"):
|
|
raise RuntimeError(result.get("errMsg") or "设备鉴权失败")
|
|
|
|
_DEVICE_TOKEN = str(result.get("deviceToken") or "").strip()
|
|
_DEVICE_TOKEN_EXPIRES_AT_MS = int(result.get("expiresAtMs") or 0)
|
|
if not _DEVICE_TOKEN or _DEVICE_TOKEN_EXPIRES_AT_MS <= now_ms:
|
|
raise RuntimeError("设备鉴权返回了无效 deviceToken")
|
|
return _DEVICE_TOKEN
|
|
|
|
|
|
def device_post(payload, timeout=10):
|
|
"""Call the device-scoped API route with an auto-renewed device token."""
|
|
token = _ensure_device_token(timeout=timeout)
|
|
request_payload = {**payload, "deviceToken": token}
|
|
response = requests.post(device_api_url, json=request_payload, timeout=timeout)
|
|
response.raise_for_status()
|
|
result = response.json()
|
|
if result.get("success"):
|
|
return result
|
|
if result.get("errCode") in {"DEVICE_TOKEN_EXPIRED", "DEVICE_TOKEN_INVALID"}:
|
|
with _DEVICE_TOKEN_LOCK:
|
|
global _DEVICE_TOKEN, _DEVICE_TOKEN_EXPIRES_AT_MS
|
|
_DEVICE_TOKEN = None
|
|
_DEVICE_TOKEN_EXPIRES_AT_MS = 0
|
|
token = _ensure_device_token(timeout=timeout)
|
|
response = requests.post(device_api_url, json={**payload, "deviceToken": token}, timeout=timeout)
|
|
response.raise_for_status()
|
|
result = response.json()
|
|
return result |