From e241a915241d30dd84f4998396d543ee8ffdf600 Mon Sep 17 00:00:00 2001 From: grigo Date: Tue, 23 Jun 2026 14:23:59 +0300 Subject: [PATCH] added proxy --- server/__pycache__/flask_app.cpython-313.pyc | Bin 5513 -> 21827 bytes server/core/config.py | 5 +++ server/flask_app.py | 37 ++++++++++++++++++- server/static/index.html | 7 ++-- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/server/__pycache__/flask_app.cpython-313.pyc b/server/__pycache__/flask_app.cpython-313.pyc index 0b8392e598cdde71e92f0ef7488205415a4e8633..ab35a93e9f6a26a5c277aca62e1bf310827455f0 100644 GIT binary patch literal 21827 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 N3C*yMD^w7L{=dZlMHm18 delta 2516 zcmZ`)-ESMm5#ReDkDnsVqbQNo2QAAMDcOoEDX}D5N|eZkOvRQs52v9UHc~rxPrUwN<&;o%`ppdKn0i9i%lp3Q1 zI5#`L+1c5d-|l|?vGDVej)QPGK=5}dd3@uez=Mu%@g4C&wtrJzl=B|&Lzs`1#g2RgBKasp`PjF3EFXhdUImqp{fqH@Cv;{= zNg5!f#LEI(n2^Ft^xFy5-yD#_S^6Llf8|V&5J;9h=$I;{Q6^jN#b7ZsB|%RqfYzRt z6@zvbrs=O|;&l6p&x^yJ!VE@I)8Up&u_HxZe!dFyBRkhOc?fCKb{LUlz$@ct1JaGg+)xt-*+Vi`Q;6ue^ia@rMDyF7?Oo$dExMTX7 z6pxi6rD!o-?3t1>WKCQXbG>xhJL{8O&!wtn-=yz)OJUKK7nX|4u6%8&uuLP$09{bt z6bA_Xwzr%9Q3-4hlAL(;ZosVR#_iE{XS13ULBZO&f#UMq^5r+m^Op<1BL}RS?xi=T zODnbkcFD3w>tL-xX|AS&W$Hy|OE;}+U~L!`$1c5TfwpYejsYcfzlEP0ETghkuaDO6 zxE{V5Br#@h0)-txagY2X*!Ro3PcAfqle_XHbfJwZk=|e!6$$?bj)+;$&}!r}|0%KE>FSQWIBMz4>o7-qH#Q&hS@f<6=&w|BTLbxE=$*0#{x)YmP}Ljty+5 z(fe*pH*T2~qipIKFn+RSf}uZs4-cFxRW%2NRdZ#qZPQgitF0O3P1g_FPs=wz+caF! zv2zM!Fb)hbae|3KCQdSOiisfW zW|M3O+>Y{%s--z)3(7UCW&mrP8wI0cfnIius#=nA{nB zlzo_eJn_M3Bba*@@rAti=Km8TA@%)pe^p1mR7al7H^S-<8o{w$d92CZfLfj61>#!{ zRZZMLvsX4S)^o4s?t-j(Eo>aIG5UM8U+AK9xg?FA_-ZACHrG=%H%;fMjyg7tw?fun z&4yuyN=S=lL-=}O7V@yZVzbHYRSrhb8<3_GHZqj9`Gg9Y#9Zz;yEq+j21!e53eCIOMjL9 zQ0V$(E+-2cuE#cO6$99Qf$jY17btQ**TX5<%6xM55qsgZVg>IMc-GVa#2_nsW#Ea9;jMT92HyV9WPty>T*tWMeAm>U?N}9l^t`_ z_&zJVc+RTMI$Xwemho@1lfbUY*v{Zb`fttOm>=H!i#pZ_j_=CjyiFB>-c#cA&sVR% zK7+nF3Fh(R`j_w2jf?#D2)w0Lx0-rRa6Px!$Y#%Tv!KmJux`9f=-W$HYS>lEW!z%^2Yxp+TRM(LU<~KvXf}*hlW)_NV5|21n)@zbI$Kp^7Aoi`u=VT}tk1qA2*TgU z_}@wJYtsE4nQf5SuSpt}Z^*zmWCV@$(;qIK5rnA&iHOmC5w(3E5j{WazTdqYNHv7? zGf@@B8QQbYwEfdmFPx4@!Ubka?~ADIhv?1XY_jWq*E_upq3an&nP6x)i#PFaA5E>C Il-bMpUoiYhdjJ3c diff --git a/server/core/config.py b/server/core/config.py index 3b62e57..60742ad 100644 --- a/server/core/config.py +++ b/server/core/config.py @@ -28,3 +28,8 @@ ELEVATION_PROBE_TTL_SEC = float( ELEVATION_CONNECT_TIMEOUT = float( os.environ.get("LORATESTER_ELEVATION_TIMEOUT", "8") ) +RF_API_URL = os.environ.get( + "LORATESTER_RF_API_URL", + "http://127.0.0.1:5603", +).rstrip("/") +RF_API_TIMEOUT = float(os.environ.get("LORATESTER_RF_API_TIMEOUT", "30")) diff --git a/server/flask_app.py b/server/flask_app.py index dba4bfb..17926a9 100644 --- a/server/flask_app.py +++ b/server/flask_app.py @@ -3,11 +3,12 @@ import time from pathlib import Path -from flask import Flask, jsonify, request, send_from_directory +import httpx +from flask import Flask, Response, jsonify, request, send_from_directory from flask_cors import CORS from core.auth import ANDROID_CLIENT_HEADER, is_android_client -from core.config import HOST, PORT +from core.config import HOST, PORT, RF_API_TIMEOUT, RF_API_URL from core.models import ChatIn, TelemetryIn from core import storage from core.telemetry_body import telemetry_from_body @@ -27,6 +28,38 @@ def index(): return resp +@app.route("/api/rf/", methods=["GET", "POST"]) +def rf_api_proxy(rf_path: str): + target = f"{RF_API_URL}/{rf_path.lstrip('/')}" + if request.query_string: + target = f"{target}?{request.query_string.decode('utf-8', errors='ignore')}" + try: + with httpx.Client(timeout=RF_API_TIMEOUT) as client: + upstream = client.request( + request.method, + target, + content=request.get_data() if request.method != "GET" else None, + headers={ + "Accept": request.headers.get("Accept", "application/json"), + "Content-Type": request.headers.get( + "Content-Type", "application/json" + ), + }, + ) + except httpx.RequestError as e: + return jsonify({ + "error": "RF API unavailable", + "detail": str(e), + "target": target, + }), 502 + + return Response( + upstream.content, + status=upstream.status_code, + content_type=upstream.headers.get("content-type", "application/json"), + ) + + @app.post("/api/telemetry") def post_telemetry(): if not is_android_client(request.headers): diff --git a/server/static/index.html b/server/static/index.html index ff3213c..1aca53d 100644 --- a/server/static/index.html +++ b/server/static/index.html @@ -2054,8 +2054,7 @@ } function defaultApiDebugBase() { - const port = location.port === '5603' ? '' : ':5603'; - return `${location.protocol}//${location.hostname}${port}`; + return '/api/rf'; } function apiDebugNumber(id, fallback = null) { @@ -2299,7 +2298,7 @@ } catch (e) { apiDebugSetStatus(`Ошибка запроса: ${e.message || e}`, true); apiDebugSetOutput(e.response || - 'Проверьте API base URL, доступность порта и CORS. Для API на том же сервере обычно подходит порт 5603.'); + 'Проверьте API base URL. По умолчанию используется same-origin proxy /api/rf.'); apiDebugSetSummary('Расчёт не выполнен.'); } } @@ -2407,7 +2406,7 @@ } catch (e) { apiDebugSetStatus(`Ошибка расчёта линии: ${e.message || e}`, true); apiDebugSetOutput(e.response || - 'Проверьте API base URL, доступность порта и CORS. Для API на том же сервере обычно подходит порт 5603.'); + 'Проверьте API base URL. По умолчанию используется same-origin proxy /api/rf.'); apiDebugSetSummary('Расчёт линии не выполнен.'); } }