Re-add translation of QRZ/HamQTH query params to header params, since it turns out someone was using that

This commit is contained in:
Ian Renton
2026-08-16 15:23:05 +01:00
parent 7220a0f58d
commit 842e91b34a
10 changed files with 60 additions and 31 deletions
+18 -15
View File
@@ -3,27 +3,39 @@ from tornado.httpclient import AsyncHTTPClient
from tornado.httputil import HTTPHeaders from tornado.httputil import HTTPHeaders
_LEGACY_PARAM_TO_HEADER_MAP = {
"qrz_username": "X-QRZ-Username",
"qrz_password": "X-QRZ-Password",
"qrz_session_key": "X-QRZ-Session-Key",
"hamqth_username": "X-HamQTH-Username",
"hamqth_password": "X-HamQTH-Password",
"hamqth_session_id": "X-HamQTH-Session-ID",
}
class V1RedirectHandler(tornado.web.RequestHandler): class V1RedirectHandler(tornado.web.RequestHandler):
"""Transparently proxies requests from the old API to the new one, """Transparently proxies requests from the old API to the new one,
returning whatever the v2 endpoint returns, for endpoints with no breaking changes.""" returning whatever the v2 endpoint returns, for endpoints with no breaking changes."""
SUPPORTED_METHODS = ("GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS")
# Methods where an HTTP body isn't valid
_BODYLESS_METHODS = {"GET", "HEAD", "OPTIONS"}
async def _proxy(self, path): async def _proxy(self, path):
new_url = f"{self.request.protocol}://{self.request.host}/api/v2/{path}" new_url = f"{self.request.protocol}://{self.request.host}/api/v2/{path}"
if self.request.query: if self.request.query:
new_url += f"?{self.request.query}" new_url += f"?{self.request.query}"
# Copy the incoming headers so we can add translated legacy credentials without changing the original
# request.
headers = HTTPHeaders(self.request.headers)
for param, header in _LEGACY_PARAM_TO_HEADER_MAP.items():
value = self.get_query_argument(param, default=None)
if value:
headers[header] = value
client = AsyncHTTPClient() client = AsyncHTTPClient()
try: try:
response = await client.fetch( response = await client.fetch(
new_url, new_url,
method=self.request.method, method=self.request.method,
headers=self.request.headers, headers=self.request.headers,
body=None if self.request.method in self._BODYLESS_METHODS else (self.request.body or b""), body=None if self.request.method == "GET" else (self.request.body or b""),
raise_error=False, raise_error=False,
follow_redirects=False, follow_redirects=False,
request_timeout=10.0, request_timeout=10.0,
@@ -49,12 +61,3 @@ class V1RedirectHandler(tornado.web.RequestHandler):
async def post(self, path): async def post(self, path):
await self._proxy(path) await self._proxy(path)
async def put(self, path):
await self._proxy(path)
async def delete(self, path):
await self._proxy(path)
async def patch(self, path):
await self._proxy(path)
+26
View File
@@ -3,11 +3,33 @@ import re
from server.handlers.api.spots import APISpotsHandler, APISpotsStreamHandler from server.handlers.api.spots import APISpotsHandler, APISpotsStreamHandler
_GRID_SOURCE_RE = re.compile(r'"dx_location_source":\s*"GRID"') _GRID_SOURCE_RE = re.compile(r'"dx_location_source":\s*"GRID"')
_LEGACY_PARAM_TO_HEADER_MAP = {
"qrz_username": "X-QRZ-Username",
"qrz_password": "X-QRZ-Password",
"qrz_session_key": "X-QRZ-Session-Key",
"hamqth_username": "X-HamQTH-Username",
"hamqth_password": "X-HamQTH-Password",
"hamqth_session_id": "X-HamQTH-Session-ID",
}
def _handle_legacy_params(handler):
"""Copy v1 query-string QRZ/HamQTH credentials into the v2 headers, so the v2 handler can see them"""
for param, header in _LEGACY_PARAM_TO_HEADER_MAP.items():
if header in handler.request.headers:
continue
value = handler.get_query_argument(param, default=None)
if value:
handler.request.headers[header] = value
class V1APISpotsHandler(APISpotsHandler): class V1APISpotsHandler(APISpotsHandler):
"""API request handler for /api/v1/spots (GET). Included in early Spothole v2 for backwards compatibility.""" """API request handler for /api/v1/spots (GET). Included in early Spothole v2 for backwards compatibility."""
def prepare(self):
_handle_legacy_params(self)
super().prepare()
def write(self, chunk): def write(self, chunk):
if isinstance(chunk, str): if isinstance(chunk, str):
chunk = _GRID_SOURCE_RE.sub('"dx_location_source": "SPOT"', chunk) chunk = _GRID_SOURCE_RE.sub('"dx_location_source": "SPOT"', chunk)
@@ -17,6 +39,10 @@ class V1APISpotsHandler(APISpotsHandler):
class V1APISpotsStreamHandler(APISpotsStreamHandler): class V1APISpotsStreamHandler(APISpotsStreamHandler):
"""API request handler for /api/v1/spots/stream (SSE). Included in early Spothole v2 for backwards compatibility.""" """API request handler for /api/v1/spots/stream (SSE). Included in early Spothole v2 for backwards compatibility."""
def prepare(self):
_handle_legacy_params(self)
super().prepare()
def write_message(self, *args, **kwargs): def write_message(self, *args, **kwargs):
args = list(args) args = list(args)
for i, a in enumerate(args): for i, a in enumerate(args):
+1 -1
View File
@@ -76,7 +76,7 @@
</div> </div>
<script src="/static/js/add-spot.js?v=1786885905"></script> <script src="/static/js/add-spot.js?v=1786890185"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-add-spot").addClass("active"); $("#nav-link-add-spot").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -84,7 +84,7 @@
</div> </div>
<script src="/static/js/alerts.js?v=1786885905"></script> <script src="/static/js/alerts.js?v=1786890185"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-alerts").addClass("active"); $("#nav-link-alerts").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -76,8 +76,8 @@
</div> </div>
<script src="/static/js/spotsbandsandmap.js?v=1786885905"></script> <script src="/static/js/spotsbandsandmap.js?v=1786890185"></script>
<script src="/static/js/bands.js?v=1786885905"></script> <script src="/static/js/bands.js?v=1786890185"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-bands").addClass("active"); $("#nav-link-bands").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+5 -5
View File
@@ -1,6 +1,6 @@
{% extends "skeleton.html" %} {% extends "skeleton.html" %}
{% block head_extra %} {% block head_extra %}
<link rel="stylesheet" href="/static/css/style.css?v=1786885905" type="text/css"> <link rel="stylesheet" href="/static/css/style.css?v=1786890185" type="text/css">
<link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet"> <link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
<link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet"> <link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet">
<link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet"> <link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
@@ -15,10 +15,10 @@
window.fetchEventSource = fetchEventSource; window.fetchEventSource = fetchEventSource;
</script> </script>
<script src="/static/js/utils.js?v=1786885905"></script> <script src="/static/js/utils.js?v=1786890185"></script>
<script src="/static/js/ui-ham.js?v=1786885905"></script> <script src="/static/js/ui-ham.js?v=1786890185"></script>
<script src="/static/js/geo.js?v=1786885905"></script> <script src="/static/js/geo.js?v=1786890185"></script>
<script src="/static/js/common.js?v=1786885905"></script> <script src="/static/js/common.js?v=1786890185"></script>
{% end %} {% end %}
{% block body %} {% block body %}
<div class="container"> <div class="container">
+1 -1
View File
@@ -284,7 +284,7 @@
</div> </div>
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script> <script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
<script src="/static/js/conditions.js?v=1786885905"></script> <script src="/static/js/conditions.js?v=1786890185"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-conditions").addClass("active"); $("#nav-link-conditions").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -109,8 +109,8 @@
<script src="/static/vendor/js/leaflet-cqzones.js"></script> <script src="/static/vendor/js/leaflet-cqzones.js"></script>
<script src="/static/vendor/js/leaflet-workedallbritainireland.js" type="module"></script> <script src="/static/vendor/js/leaflet-workedallbritainireland.js" type="module"></script>
<script src="/static/js/spotsbandsandmap.js?v=1786885905"></script> <script src="/static/js/spotsbandsandmap.js?v=1786890185"></script>
<script src="/static/js/map.js?v=1786885905"></script> <script src="/static/js/map.js?v=1786890185"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-map").addClass("active"); $("#nav-link-map").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -113,8 +113,8 @@
</div> </div>
<script src="/static/js/spotsbandsandmap.js?v=1786885905"></script> <script src="/static/js/spotsbandsandmap.js?v=1786890185"></script>
<script src="/static/js/spots.js?v=1786885905"></script> <script src="/static/js/spots.js?v=1786890185"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-spots").addClass("active"); $("#nav-link-spots").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -86,7 +86,7 @@
</div> </div>
</div> </div>
<script src="/static/js/status.js?v=1786885905"></script> <script src="/static/js/status.js?v=1786890185"></script>
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$("#nav-link-status").addClass("active"); $("#nav-link-status").addClass("active");