"""Start the source workstation against a loopback backend with isolated login state.""" from __future__ import annotations import functools import os import socket import sys from dataclasses import replace from pathlib import Path from urllib.parse import urlsplit import certifi def validate_local_url(api: str) -> None: parsed = urlsplit(api) if (parsed.scheme != "https" or not parsed.hostname or parsed.username or parsed.password or parsed.query or parsed.fragment): raise ValueError("本地入口需要不带凭据的 HTTPS 地址") addresses = {row[4][0] for row in socket.getaddrinfo(parsed.hostname, parsed.port or 443)} if not addresses or not addresses <= {"127.0.0.1", "::1"}: raise ValueError("本地入口仅连接解析到本机的后端") def validate_effective_config(config, requested_api: str) -> None: from doctor_workstation.config import normalize_api_base_url validate_local_url(config.api_base_url) if config.api_base_url != normalize_api_base_url(requested_api) or not config.verify_ssl: raise ValueError("本地独立配置已改动,请在 .local-workstation/config 中核对本地服务器设置") def local_config_type(api: str): """Pin this explicit local entry, including later login/config updates.""" from doctor_workstation.config import AppConfig, normalize_api_base_url validate_local_url(api) local_api = normalize_api_base_url(api) class LocalAppConfig(AppConfig): def _merge_preferences(self): config = super()._merge_preferences() # Use the locked login behavior with the selected local endpoint. # Shared Qt settings and stale local preferences must not send a # local verification session back to the production backend. return replace(config, api_base_url=local_api, debug_mode=False, demo_mode=False, verify_ssl=True) return LocalAppConfig def main() -> int: # Select the native backend before Qt creates network objects or scans TLS # plugins. Selecting it later can load conflicting OpenSSL DLLs on Windows. if sys.platform == "win32": os.environ["QT_TLS_BACKEND"] = "schannel" root = Path(__file__).resolve().parents[1] api = os.environ.get("DOCTOR_LOCAL_API_URL", "https://css.zhenyangtang.com.cn") validate_local_url(api) certificate = Path(os.environ.get( "DOCTOR_LOCAL_CA_FILE", "D:/phpstudy_pro/Extensions/Nginx1.15.11/conf/ssl/css.zhenyangtang.com.cn.pem", )) private_root = root / ".local-workstation" private_root.mkdir(parents=True, exist_ok=True) ca_bundle = private_root / "trusted-ca.pem" ca_bundle.write_bytes(Path(certifi.where()).read_bytes() + b"\n" + certificate.read_bytes()) os.environ.update({ "DOCTOR_CONFIG_DIR": str(private_root / "config"), "DOCTOR_LOG_DIR": str(private_root / "logs"), "DOCTOR_API_BASE_URL": api, "DOCTOR_DEMO_MODE": "false", "DOCTOR_VERIFY_SSL": "true", "SSL_CERT_FILE": str(ca_bundle), }) sys.path.insert(0, str(root / "src")) # This explicit source-only entry does not edit the formal build configuration. LocalAppConfig = local_config_type(api) config = LocalAppConfig.load() validate_effective_config(config, api) config.save_preferences() if "--check" in sys.argv: print("Local backend: loopback HTTPS; TLS verification enabled; isolated configuration and credential namespace") return 0 from PySide6.QtCore import QSettings from doctor_workstation import app as workstation_app workstation_app.AppConfig = LocalAppConfig workstation_app.LoginWindow = functools.partial( workstation_app.LoginWindow, settings=QSettings("ZhenYangTang", "DoctorWorkstationLocalSource"), ) # The normal credential service remains untouched even after a local login. workstation_app.TokenStore = functools.partial( workstation_app.TokenStore, service_name="zyt-doctor-workstation-local-source" ) return workstation_app.main() if __name__ == "__main__": raise SystemExit(main())