v1.0.16.13 — TSPU/SNI-obkhod: domain selection at login + dynamic mirrors

- add DomainSelector: probe every API candidate at launcher startup, pick
  fastest reachable (primary api.zern.cc, legacy .ru/.online, geo pl/swe);
  integrate into CLI start and JFX network init
- Config.setServerUrl() + persist chosen domain in launcher.properties
- ZHttpClient: default BASE_URL and ZERN_SERVER health-check follow the
  selected domain (runtime failover instead of hardcoded api.zernmc.ru)
- server: LAUNCHER_MIRRORS main=api.zern.cc (primary), legacy .ru/.online,
  geo-pl api.pl.zern.cc, geo-swe api.swe.zern.cc; /launcher/mirrors exposes them
- diag: check all TLD+geo API hosts (api.{pl,ru,swe}.zern.cc, .ru, .online)
  for DNS/TCP/HTTP, known server IPs for all 4 geo nodes
- reverse-proxy geo boxes (pl, swe) -> main:1582 so clients bypass TSPU via
  an unblocked SNI; ru left as-is (VLESS VPN box, not proxying API)
This commit is contained in:
SashegDev
2026-08-18 22:19:55 +00:00
parent ecb49e1eb7
commit a2073044e9
40 changed files with 2233 additions and 252 deletions
+9
View File
@@ -383,6 +383,7 @@ async def register(body: RegisterRequest, request: Request):
allowed, wait = check_rate_limit(ip)
if not allowed:
logger.warning("register rate limited", username=body.username, client_ip=ip)
raise HTTPException(429, f"Слишком много попыток. Подождите {wait} секунд")
with get_db() as conn:
@@ -392,6 +393,7 @@ async def register(body: RegisterRequest, request: Request):
).fetchone()
if existing:
logger.warning("register failed: username taken", username=body.username, client_ip=ip)
raise HTTPException(409, "Пользователь с таким именем уже существует")
uuid = generate_uuid()
@@ -405,6 +407,7 @@ async def register(body: RegisterRequest, request: Request):
)
user_id = cursor.lastrowid
logger.info("register ok", username=body.username, user_id=user_id, client_ip=ip)
# Создаем сессию
session_token = secrets.token_urlsafe(32)
@@ -456,6 +459,7 @@ async def login(body: LoginRequest, request: Request):
allowed, wait = check_rate_limit(ip)
if not allowed:
logger.warning("login rate limited", username=body.username, client_ip=ip)
raise HTTPException(429, f"Слишком много попыток. Подождите {wait} секунд")
with get_db() as conn:
@@ -465,15 +469,20 @@ async def login(body: LoginRequest, request: Request):
).fetchone()
if not user or not verify_password(body.password, user["password_hash"]):
logger.warning("login failed: bad credentials", username=body.username, client_ip=ip)
record_login_attempt(ip, False)
raise HTTPException(401, "Неверное имя пользователя или пароль")
if not user["is_active"]:
logger.warning("login failed: account deactivated", username=body.username, client_ip=ip)
raise HTTPException(403, "Аккаунт деактивирован")
if user["banned_until"] and user["banned_until"] > time.time():
logger.warning("login failed: account banned", username=body.username, client_ip=ip)
raise HTTPException(403, "Аккаунт забанен")
logger.info("login ok", username=user["username"], user_id=user["id"], client_ip=ip)
record_login_attempt(ip, True)
now = time.time()