3 Commits
12 changed files with 145 additions and 97 deletions
+42 -6
View File
@@ -310,44 +310,80 @@ server {
# SSE endpoints
location ~ ^/api/v1/(spots|alerts)/stream {
proxy_pass http://127.0.0.1:8080;
# Allow keep-alive
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://127.0.0.1:8080;
# Set correct content type for SSE API calls
add_header Content-Type text/event-stream always;
# Set remove buffering, remove caching, add suitable timeouts for SSE API calls
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 24h;
proxy_connect_timeout 10s;
proxy_send_timeout 24h;
proxy_set_header X-Accel-Buffering no;
add_header Cache-Control no-store always;
# Allow cross-origin requests to API
proxy_hide_header Access-Control-Allow-Origin;
add_header Access-Control-Allow-Origin * always;
add_header Cache-Control no-store always;
add_header Content-Type text/event-stream always;
# Pass on IP address and host information to Spothole, in case logging this information is required
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Other API endpoints
location /api/ {
proxy_pass http://127.0.0.1:8080;
# Allow keep-alive
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://127.0.0.1:8080;
# Set up buffering, remove caching, add suitable timeouts for API calls
proxy_buffering on;
proxy_cache off;
proxy_read_timeout 30s;
proxy_connect_timeout 10s;
add_header Cache-Control no-store always;
# Allow cross-origin requests to API
proxy_hide_header Access-Control-Allow-Origin;
add_header Access-Control-Allow-Origin * always;
add_header Cache-Control no-store always;
# Pass on IP address and host information to Spothole, in case logging this information is required
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Static assets
location / {
proxy_pass http://127.0.0.1:8080;
# Allow keep-alive
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://127.0.0.1:8080;
# Set up buffering and caching, add suitable timeouts for static asset requests
proxy_buffering on;
proxy_read_timeout 30s;
proxy_connect_timeout 10s;
add_header Cache-Control "public, max-age=3600, must-revalidate" always;
# Pass on IP address and host information to Spothole, in case logging this information is required
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
+21 -1
View File
@@ -108,8 +108,9 @@ class WebServer:
app = tornado.web.Application(api_routes + ui_routes + misc_routes,
template_path=os.path.join(_HERE, "../templates"),
log_function=request_log,
debug=False)
app.listen(self._port)
app.listen(self._port, xheaders=True)
logging.info("Web server running on port " + str(WEB_SERVER_PORT))
await self._shutdown_event.wait()
@@ -162,3 +163,22 @@ class WebServer:
# Probably got deleted already on another thread
pass
pass
def request_log(handler):
"""Custom log function to provide more data about requests."""
if handler.get_status() < 500:
log_method = logging.info
else:
log_method = logging.warning
request = handler.request
client_ip = request.remote_ip
referrer = request.headers.get("Referer", "-")
user_agent = request.headers.get("User-Agent", "-")
log_method(
f'{client_ip} - "{request.method} {request.uri}" '
f'{handler.get_status()} {request.request_time():.2f}ms | '
f'Ref: {referrer} | UA: {user_agent}'
)
+63 -62
View File
@@ -24,68 +24,69 @@ class GMA(HTTPSpotProvider):
def _http_response_to_spots(self, http_response):
new_spots = []
# Iterate through source data
for source_spot in http_response.json()["RCD"]:
# Convert to our spot format
spot = Spot(source=self.name,
dx_call=source_spot["ACTIVATOR"].upper(),
de_call=source_spot["SPOTTER"].upper(),
freq=float(source_spot["QRG"]) * 1000 if (source_spot["QRG"] != "") else None,
# Seen GMA spots with no frequency
mode=source_spot["MODE"].upper() if "<>" not in source_spot["MODE"] else None,
# Filter out some weird mode strings
comment=source_spot["TEXT"],
sig_refs=[SIGRef(id=source_spot["REF"], sig="", name=source_spot["NAME"])],
time=datetime.strptime(source_spot["DATE"] + source_spot["TIME"], "%Y%m%d%H%M").replace(
tzinfo=pytz.UTC).timestamp(),
dx_latitude=float(source_spot["LAT"]) if (
source_spot["LAT"] and source_spot["LAT"] != "") else None,
# Seen GMA spots with no (or empty) lat/lon
dx_longitude=float(source_spot["LON"]) if (
source_spot["LON"] and source_spot["LON"] != "") else None)
if "RCD" in http_response.json():
for source_spot in http_response.json()["RCD"]:
# Convert to our spot format
spot = Spot(source=self.name,
dx_call=source_spot["ACTIVATOR"].upper(),
de_call=source_spot["SPOTTER"].upper(),
freq=float(source_spot["QRG"]) * 1000 if (source_spot["QRG"] != "") else None,
# Seen GMA spots with no frequency
mode=source_spot["MODE"].upper() if "<>" not in source_spot["MODE"] else None,
# Filter out some weird mode strings
comment=source_spot["TEXT"],
sig_refs=[SIGRef(id=source_spot["REF"], sig="", name=source_spot["NAME"])],
time=datetime.strptime(source_spot["DATE"] + source_spot["TIME"], "%Y%m%d%H%M").replace(
tzinfo=pytz.UTC).timestamp(),
dx_latitude=float(source_spot["LAT"]) if (
source_spot["LAT"] and source_spot["LAT"] != "") else None,
# Seen GMA spots with no (or empty) lat/lon
dx_longitude=float(source_spot["LON"]) if (
source_spot["LON"] and source_spot["LON"] != "") else None)
# GMA doesn't give what programme (SIG) the reference is for until we separately look it up.
if "REF" in source_spot:
try:
ref_response = SEMI_STATIC_URL_DATA_CACHE.get(self.REF_INFO_URL_ROOT + source_spot["REF"],
headers=HTTP_HEADERS)
# Sometimes this is blank, so handle that
if ref_response.text is not None and ref_response.text != "":
ref_info = ref_response.json()
# If this is POTA, SOTA or WWFF data we already have it through other means, so ignore. POTA and WWFF
# spots come through with reftype=POTA or reftype=WWFF. SOTA is harder to figure out because both SOTA
# and GMA summits come through with reftype=Summit, so we must check for the presence of a "sota" entry
# to determine if it's a SOTA summit.
if spot.sig_refs and "reftype" in ref_info and ref_info["reftype"] not in ["POTA", "WWFF"] and (
ref_info["reftype"] != "Summit" or "sota" not in ref_info or ref_info["sota"] == ""):
match ref_info["reftype"]:
case "Summit":
spot.sig_refs[0].sig = "GMA"
spot.sig = "GMA"
case "IOTA Island":
spot.sig_refs[0].sig = "IOTA"
spot.sig = "IOTA"
case "Lighthouse (ILLW)":
spot.sig_refs[0].sig = "ILLW"
spot.sig = "ILLW"
case "Lighthouse (ARLHS)":
spot.sig_refs[0].sig = "ARLHS"
spot.sig = "ARLHS"
case "Castle":
spot.sig_refs[0].sig = "WCA"
spot.sig = "WCA"
case "Mill":
spot.sig_refs[0].sig = "MOTA"
spot.sig = "MOTA"
case _:
logging.warning("GMA spot found with ref type " + ref_info[
"reftype"] + ", developer needs to add support for this!")
spot.sig_refs[0].sig = ref_info["reftype"]
spot.sig = ref_info["reftype"]
# GMA doesn't give what programme (SIG) the reference is for until we separately look it up.
if "REF" in source_spot:
try:
ref_response = SEMI_STATIC_URL_DATA_CACHE.get(self.REF_INFO_URL_ROOT + source_spot["REF"],
headers=HTTP_HEADERS)
# Sometimes this is blank, so handle that
if ref_response.text is not None and ref_response.text != "":
ref_info = ref_response.json()
# If this is POTA, SOTA or WWFF data we already have it through other means, so ignore. POTA and WWFF
# spots come through with reftype=POTA or reftype=WWFF. SOTA is harder to figure out because both SOTA
# and GMA summits come through with reftype=Summit, so we must check for the presence of a "sota" entry
# to determine if it's a SOTA summit.
if spot.sig_refs and "reftype" in ref_info and ref_info["reftype"] not in ["POTA", "WWFF"] and (
ref_info["reftype"] != "Summit" or "sota" not in ref_info or ref_info["sota"] == ""):
match ref_info["reftype"]:
case "Summit":
spot.sig_refs[0].sig = "GMA"
spot.sig = "GMA"
case "IOTA Island":
spot.sig_refs[0].sig = "IOTA"
spot.sig = "IOTA"
case "Lighthouse (ILLW)":
spot.sig_refs[0].sig = "ILLW"
spot.sig = "ILLW"
case "Lighthouse (ARLHS)":
spot.sig_refs[0].sig = "ARLHS"
spot.sig = "ARLHS"
case "Castle":
spot.sig_refs[0].sig = "WCA"
spot.sig = "WCA"
case "Mill":
spot.sig_refs[0].sig = "MOTA"
spot.sig = "MOTA"
case _:
logging.warning("GMA spot found with ref type " + ref_info[
"reftype"] + ", developer needs to add support for this!")
spot.sig_refs[0].sig = ref_info["reftype"]
spot.sig = ref_info["reftype"]
# Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do
# that for us.
new_spots.append(spot)
except:
logging.warning("Exception when looking up " + self.REF_INFO_URL_ROOT + source_spot[
"REF"] + ", ignoring this spot for now")
# Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do
# that for us.
new_spots.append(spot)
except:
logging.warning("Exception when looking up " + self.REF_INFO_URL_ROOT + source_spot[
"REF"] + ", ignoring this spot for now")
return new_spots
+1 -1
View File
@@ -13,7 +13,7 @@
href="/apidocs">API documentation</a>. The API delivers spots in a consistent format regardless of the data
source, freeing developers from needing to know how each individual data source presents its data.</p>
<p>Spothole itself is also open source, Public Domain licenced code that anyone can take and modify. <a
href="https://git.ianrenton.com/ian/metaspot/">The source code is here</a>.</p>
href="https://git.ianrenton.com/ian/spothole/">The source code is here</a>.</p>
<p>The software was written by <a href="https://ianrenton.com">Ian Renton, MØTRT</a> and other contributors. Full
details are available in the <a href="https://git.ianrenton.com/ian/spothole/src/branch/main/README.md">README
file</a>.</p>
+1 -1
View File
@@ -76,7 +76,7 @@
</div>
<script src="/js/add-spot.js?v=1781901371"></script>
<script src="/js/add-spot.js?v=1781955606"></script>
<script>$(document).ready(function () {
$("#nav-link-add-spot").addClass("active");
}); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -75,7 +75,7 @@
</div>
<script src="/js/alerts.js?v=1781901371"></script>
<script src="/js/alerts.js?v=1781955606"></script>
<script>$(document).ready(function () {
$("#nav-link-alerts").addClass("active");
}); <!-- highlight active page in nav --></script>
+3 -6
View File
@@ -75,13 +75,10 @@
</div>
<script>
let spotProvidersEnabledByDefault = { % raw
json_encode(web_ui_options["spot-providers-enabled-by-default"]) %
}
;
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
</script>
<script src="/js/spotsbandsandmap.js?v=1781901371"></script>
<script src="/js/bands.js?v=1781901371"></script>
<script src="/js/spotsbandsandmap.js?v=1781955606"></script>
<script src="/js/bands.js?v=1781955606"></script>
<script>$(document).ready(function () {
$("#nav-link-bands").addClass("active");
}); <!-- highlight active page in nav --></script>
+5 -5
View File
@@ -1,6 +1,6 @@
{% extends "skeleton.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/css/style.css?v=1781901371" type="text/css">
<link rel="stylesheet" href="/css/style.css?v=1781955606" type="text/css">
<link href="/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
<link href="/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet">
<link href="/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
@@ -10,10 +10,10 @@
<script src="/vendor/js/bootstrap-5.3.8.bundle.min.js"></script>
<script src="/vendor/js/tinycolor2-1.6.0.min.js"></script>
<script src="/js/utils.js?v=1781901371"></script>
<script src="/js/ui-ham.js?v=1781901371"></script>
<script src="/js/geo.js?v=1781901371"></script>
<script src="/js/common.js?v=1781901371"></script>
<script src="/js/utils.js?v=1781955606"></script>
<script src="/js/ui-ham.js?v=1781955606"></script>
<script src="/js/geo.js?v=1781955606"></script>
<script src="/js/common.js?v=1781955606"></script>
{% end %}
{% block body %}
<div class="container">
+1 -1
View File
@@ -284,7 +284,7 @@
</div>
<script src="/vendor/js/chart-4.4.9.umd.min.js"></script>
<script src="/js/conditions.js?v=1781901371"></script>
<script src="/js/conditions.js?v=1781955606"></script>
<script>$(document).ready(function () {
$("#nav-link-conditions").addClass("active");
}); <!-- highlight active page in nav --></script>
+3 -6
View File
@@ -93,13 +93,10 @@
<script src="/vendor/js/leaflet-workedallbritainireland.js"></script>
<script>
let spotProvidersEnabledByDefault = { % raw
json_encode(web_ui_options["spot-providers-enabled-by-default"]) %
}
;
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
</script>
<script src="/js/spotsbandsandmap.js?v=1781901371"></script>
<script src="/js/map.js?v=1781901371"></script>
<script src="/js/spotsbandsandmap.js?v=1781955606"></script>
<script src="/js/map.js?v=1781955606"></script>
<script>$(document).ready(function () {
$("#nav-link-map").addClass("active");
}); <!-- highlight active page in nav --></script>
+3 -6
View File
@@ -114,13 +114,10 @@
</div>
<script>
let spotProvidersEnabledByDefault = { % raw
json_encode(web_ui_options["spot-providers-enabled-by-default"]) %
}
;
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
</script>
<script src="/js/spotsbandsandmap.js?v=1781901371"></script>
<script src="/js/spots.js?v=1781901371"></script>
<script src="/js/spotsbandsandmap.js?v=1781955606"></script>
<script src="/js/spots.js?v=1781955606"></script>
<script>$(document).ready(function () {
$("#nav-link-spots").addClass("active");
}); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -59,7 +59,7 @@
</div>
</div>
<script src="/js/status.js?v=1781901371"></script>
<script src="/js/status.js?v=1781955606"></script>
<script>
$(document).ready(function () {
$("#nav-link-status").addClass("active");