mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-02-04 09:14:30 +00:00
118 lines
4.9 KiB
Python
118 lines
4.9 KiB
Python
import asyncio
|
|
import os
|
|
|
|
import tornado
|
|
from tornado.web import StaticFileHandler
|
|
|
|
from server.handlers.api.addspot import APISpotHandler
|
|
from server.handlers.api.alerts import APIAlertsHandler, APIAlertsStreamHandler
|
|
from server.handlers.api.lookups import APILookupCallHandler, APILookupSIGRefHandler
|
|
from server.handlers.api.options import APIOptionsHandler
|
|
from server.handlers.api.spots import APISpotsHandler, APISpotsStreamHandler
|
|
from server.handlers.api.status import APIStatusHandler
|
|
from server.handlers.metrics import PrometheusMetricsHandler
|
|
from server.handlers.pagetemplate import PageTemplateHandler
|
|
|
|
|
|
# Provides the public-facing web server.
|
|
# TODO test lookups
|
|
# TODO SSE API responses
|
|
# TODO clean_up_sse_queues
|
|
# TODO page & API access counters - how to do from a subclass handler? e.g.
|
|
# self.last_api_access_time = datetime.now(pytz.UTC)
|
|
# self.api_access_counter += 1
|
|
# api_requests_counter.inc()
|
|
# self.status = "OK"
|
|
#
|
|
# self.last_page_access_time = datetime.now(pytz.UTC)
|
|
# self.page_access_counter += 1
|
|
# page_requests_counter.inc()
|
|
# self.status = "OK"
|
|
|
|
class WebServer:
|
|
# Constructor
|
|
def __init__(self, spots, alerts, status_data, port):
|
|
self.last_page_access_time = None
|
|
self.last_api_access_time = None
|
|
self.page_access_counter = 0
|
|
self.api_access_counter = 0
|
|
self.spots = spots
|
|
self.alerts = alerts
|
|
self.sse_spot_queues = []
|
|
self.sse_alert_queues = []
|
|
self.status_data = status_data
|
|
self.port = port
|
|
self.status = "Starting"
|
|
self.shutdown_event = asyncio.Event()
|
|
|
|
# Start the web server
|
|
def start(self):
|
|
asyncio.run(self.start_inner())
|
|
|
|
# Stop the web server
|
|
def stop(self):
|
|
self.shutdown_event.set()
|
|
|
|
# Start method (async). Sets up the Tornado application.
|
|
async def start_inner(self):
|
|
app = tornado.web.Application([
|
|
# Routes for API calls
|
|
(r"/api/v1/spots", APISpotsHandler, {"spots": self.spots}),
|
|
(r"/api/v1/alerts", APIAlertsHandler, {"alerts": self.alerts}),
|
|
(r"/api/v1/spots/stream", APISpotsStreamHandler), # todo provide queues?
|
|
(r"/api/v1/alerts/stream", APIAlertsStreamHandler), # todo provide queues?
|
|
(r"/api/v1/options", APIOptionsHandler, {"status_data": self.status_data}),
|
|
(r"/api/v1/status", APIStatusHandler, {"status_data": self.status_data}),
|
|
(r"/api/v1/lookup/call", APILookupCallHandler),
|
|
(r"/api/v1/lookup/sigref", APILookupSIGRefHandler),
|
|
(r"/api/v1/spot", APISpotHandler, {"spots": self.spots}),
|
|
# Routes for templated pages
|
|
(r"/", PageTemplateHandler, {"template_name": "spots"}),
|
|
(r"/map", PageTemplateHandler, {"template_name": "map"}),
|
|
(r"/bands", PageTemplateHandler, {"template_name": "bands"}),
|
|
(r"/alerts", PageTemplateHandler, {"template_name": "alerts"}),
|
|
(r"/add-spot", PageTemplateHandler, {"template_name": "add_spot"}),
|
|
(r"/status", PageTemplateHandler, {"template_name": "status"}),
|
|
(r"/about", PageTemplateHandler, {"template_name": "about"}),
|
|
(r"/apidocs", PageTemplateHandler, {"template_name": "apidocs"}),
|
|
# Route for Prometheus metrics
|
|
(r"/metrics", PrometheusMetricsHandler),
|
|
# Default route to serve from "webassets"
|
|
(r"/(.*)", StaticFileHandler, {"path": os.path.join(os.path.dirname(__file__), "../webassets")}),
|
|
],
|
|
template_path=os.path.join(os.path.dirname(__file__), "../templates"),
|
|
debug=True) # todo set false
|
|
app.listen(self.port)
|
|
await self.shutdown_event.wait()
|
|
|
|
# Internal method called when a new spot is added to the system. This is used to ping any SSE clients that are
|
|
# awaiting a server-sent message with new spots.
|
|
def notify_new_spot(self, spot):
|
|
# todo
|
|
# for queue in self.sse_spot_queues:
|
|
# try:
|
|
# queue.put(spot)
|
|
# except:
|
|
# # Cleanup thread was probably deleting the queue, that's fine
|
|
# pass
|
|
pass
|
|
|
|
# Internal method called when a new alert is added to the system. This is used to ping any SSE clients that are
|
|
# awaiting a server-sent message with new spots.
|
|
def notify_new_alert(self, alert):
|
|
# todo
|
|
# for queue in self.sse_alert_queues:
|
|
# try:
|
|
# queue.put(alert)
|
|
# except:
|
|
# # Cleanup thread was probably deleting the queue, that's fine
|
|
# pass
|
|
pass
|
|
|
|
# Clean up any SSE queues that are growing too large; probably their client disconnected.
|
|
def clean_up_sse_queues(self):
|
|
# todo
|
|
# self.sse_spot_queues = [q for q in self.sse_spot_queues if not q.full()]
|
|
# self.sse_alert_queues = [q for q in self.sse_alert_queues if not q.full()]
|
|
pass
|