diff --git a/README.md b/README.md index 2a9893b..a66f0fd 100644 --- a/README.md +++ b/README.md @@ -309,7 +309,7 @@ server { } # SSE endpoints - location ~ ^/api/v1/(spots|alerts)/stream { + location ~ ^/api/v2/(spots|alerts)/stream { proxy_http_version 1.1; proxy_set_header Connection ""; proxy_pass http://127.0.0.1:8080; diff --git a/config-example.yml b/config-example.yml index 5943b5d..d2d9913 100644 --- a/config-example.yml +++ b/config-example.yml @@ -154,7 +154,7 @@ alert-providers: # Solar condition providers to use. These poll external APIs for solar propagation data (SFI, A/K indices, band -# conditions, etc.) and make it available via the /api/v1/solar endpoint. +# conditions, etc.) and make it available via the /api/v2/solar endpoint. solar-condition-providers: - class: "HamQSL" name: "HamQSL" diff --git a/server/handlers/api/addspot.py b/server/handlers/api/addspot.py index 0ea8af0..f939607 100644 --- a/server/handlers/api/addspot.py +++ b/server/handlers/api/addspot.py @@ -25,7 +25,7 @@ RECAPTCHA_VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify" class APISpotHandler(tornado.web.RequestHandler): - """API request handler for /api/v1/spot (POST)""" + """API request handler for /api/v2/spot (POST)""" def __init__(self, application: "Application", request: httputil.HTTPServerRequest, **kwargs: Any): self._spots = None @@ -76,13 +76,15 @@ class APISpotHandler(tornado.web.RequestHandler): # Read in the request body as JSON json_body = tornado.escape.json_decode(post_data) - # Extract fields relating to how we handle the spot, such as CAPTCHA and upstream submission. Remove these - # from the data so they don't accidentally end up in the spot object itself. - # todo: Better way of separating these out. Possible without API change or not? - submit_upstream = json_body.pop("submit_upstream", False) - upstream_provider_name = json_body.pop("upstream_provider", None) - upstream_credentials = json_body.pop("upstream_credentials", {}) - captcha_token = json_body.pop("captcha_token", None) + # Extract the "spot" and "handling" sub-objects from the request body + spot_data = json_body.get("spot", {}) + handling = json_body.get("handling", {}) + + # Extract individual parameters that say how this spot should be handled by the server + submit_upstream = handling.get("submit_upstream", False) + upstream_provider_name = handling.get("upstream_provider", None) + upstream_credentials = handling.get("upstream_credentials", {}) + captcha_token = handling.get("captcha_token", None) # Verify CAPTCHA if required if RECAPTCHA_SECRET_KEY: @@ -101,8 +103,8 @@ class APISpotHandler(tornado.web.RequestHandler): self.set_header("Content-Type", "application/json") return - # Convert remaining fields to a Spot object - spot = Spot(**json_body) + # Convert spot field to a Spot object + spot = Spot(**spot_data) # Converting to a spot object this way won't have coped with sig_ref objects, so fix that. (Would be nice to # redo this in a functional style) diff --git a/server/handlers/api/alerts.py b/server/handlers/api/alerts.py index f91d6f1..3649fde 100644 --- a/server/handlers/api/alerts.py +++ b/server/handlers/api/alerts.py @@ -20,7 +20,7 @@ SSE_HANDLER_QUEUE_CHECK_INTERVAL = 5000 class APIAlertsHandler(tornado.web.RequestHandler): - """API request handler for /api/v1/alerts""" + """API request handler for /api/v2/alerts""" def __init__(self, application: "Application", request: httputil.HTTPServerRequest, **kwargs: Any): self._alerts = None @@ -72,7 +72,7 @@ class APIAlertsHandler(tornado.web.RequestHandler): class APIAlertsStreamHandler(tornado_eventsource.handler.EventSourceHandler): - """API request handler for /api/v1/alerts/stream""" + """API request handler for /api/v2/alerts/stream""" def __init__(self, application, request, **kwargs: Any): self._sse_alert_queues = None diff --git a/server/handlers/api/dxstats.py b/server/handlers/api/dxstats.py index e6f3b03..be43eda 100644 --- a/server/handlers/api/dxstats.py +++ b/server/handlers/api/dxstats.py @@ -17,7 +17,7 @@ BANDS_SET = frozenset(BANDS) class APIDxStatsHandler(tornado.web.RequestHandler): - """API request handler for /api/v1/dxstats""" + """API request handler for /api/v2/dxstats""" def __init__(self, application: "Application", request: httputil.HTTPServerRequest, **kwargs: Any): self._spots = None diff --git a/server/handlers/api/lookups.py b/server/handlers/api/lookups.py index e885c07..3672269 100644 --- a/server/handlers/api/lookups.py +++ b/server/handlers/api/lookups.py @@ -20,7 +20,7 @@ from data.spot import Spot class APILookupCallHandler(tornado.web.RequestHandler): - """API request handler for /api/v1/lookup/call""" + """API request handler for /api/v2/lookup/call""" def __init__(self, application: "Application", request: httputil.HTTPServerRequest, **kwargs: Any): self._web_server_metrics = None @@ -85,7 +85,7 @@ class APILookupCallHandler(tornado.web.RequestHandler): class APILookupSIGRefHandler(tornado.web.RequestHandler): - """API request handler for /api/v1/lookup/sigref""" + """API request handler for /api/v2/lookup/sigref""" def __init__(self, application: "Application", request: httputil.HTTPServerRequest, **kwargs: Any): self._web_server_metrics = None @@ -139,7 +139,7 @@ class APILookupSIGRefHandler(tornado.web.RequestHandler): class APILookupGridHandler(tornado.web.RequestHandler): - """API request handler for /api/v1/lookup/grid""" + """API request handler for /api/v2/lookup/grid""" def __init__(self, application: "Application", request: httputil.HTTPServerRequest, **kwargs: Any): self._web_server_metrics = None diff --git a/server/handlers/api/options.py b/server/handlers/api/options.py index fb9f6be..7b194ca 100644 --- a/server/handlers/api/options.py +++ b/server/handlers/api/options.py @@ -14,7 +14,7 @@ from core.utils import serialize_everything class APIOptionsHandler(tornado.web.RequestHandler): - """API request handler for /api/v1/options""" + """API request handler for /api/v2/options""" def __init__(self, application: "Application", request: httputil.HTTPServerRequest, **kwargs: Any): self._status_data = None diff --git a/server/handlers/api/solar_conditions.py b/server/handlers/api/solar_conditions.py index 5fdcc46..5b2f49d 100644 --- a/server/handlers/api/solar_conditions.py +++ b/server/handlers/api/solar_conditions.py @@ -10,7 +10,7 @@ from core.prometheus_metrics_handler import api_requests_counter class APISolarConditionsHandler(tornado.web.RequestHandler): - """API request handler for /api/v1/solar""" + """API request handler for /api/v2/solar""" def __init__(self, application: "Application", request: httputil.HTTPServerRequest, **kwargs: Any): self._solar_conditions = None diff --git a/server/handlers/api/spots.py b/server/handlers/api/spots.py index ab6d3e2..6695347 100644 --- a/server/handlers/api/spots.py +++ b/server/handlers/api/spots.py @@ -20,7 +20,7 @@ SSE_HANDLER_QUEUE_CHECK_INTERVAL = 5000 class APISpotsHandler(tornado.web.RequestHandler): - """API request handler for /api/v1/spots""" + """API request handler for /api/v2/spots""" def __init__(self, application: "Application", request: httputil.HTTPServerRequest, **kwargs: Any): self._spots = None @@ -72,7 +72,7 @@ class APISpotsHandler(tornado.web.RequestHandler): class APISpotsStreamHandler(tornado_eventsource.handler.EventSourceHandler): - """API request handler for /api/v1/spots/stream""" + """API request handler for /api/v2/spots/stream""" def __init__(self, application, request, **kwargs: Any): self._sse_spot_queues = None diff --git a/server/handlers/api/status.py b/server/handlers/api/status.py index eb3efb9..3594cdf 100644 --- a/server/handlers/api/status.py +++ b/server/handlers/api/status.py @@ -12,7 +12,7 @@ from core.utils import serialize_everything class APIStatusHandler(tornado.web.RequestHandler): - """API request handler for /api/v1/status""" + """API request handler for /api/v2/status""" def __init__(self, application: "Application", request: httputil.HTTPServerRequest, **kwargs: Any): self._status_data = None diff --git a/server/handlers/api/v1_compatability.py b/server/handlers/api/v1_compatability.py new file mode 100644 index 0000000..9639bbc --- /dev/null +++ b/server/handlers/api/v1_compatability.py @@ -0,0 +1,31 @@ +import json + +import tornado + +from core.utils import serialize_everything + + +class V1GoneHandler(tornado.web.RequestHandler): + """Returns 410 Gone with a message for any endpoints in the old API that have breaking changes in the new one or + have been retired.""" + + def post(self): + self.set_status(410) + self.write(json.dumps( + "This API endpoint has a breaking change or has been removed in the current version of the Spothole API. Please see /apidocs for details of the current API version and the endpoints available.", + default=serialize_everything + )) + self.set_header("Cache-Control", "no-store") + self.set_header("Content-Type", "application/json") + + +class V1RedirectHandler(tornado.web.RequestHandler): + """Returns 308 Permanent Redirect from any path in the old API to the new one, where there were no breaking changes.""" + + def get(self, path): + new_url = "/api/v2/" + path + if self.request.query: + new_url += "?" + self.request.query + self.set_status(308) + self.set_header("Location", new_url) + self.finish() diff --git a/server/webserver.py b/server/webserver.py index 27c28f3..2e17f17 100644 --- a/server/webserver.py +++ b/server/webserver.py @@ -8,6 +8,7 @@ from tornado.web import StaticFileHandler from core.config import ALLOW_SPOTTING, WEB_SERVER_PORT, API_ONLY_MODE from core.utils import empty_queue from server.handlers.api.addspot import APISpotHandler +from server.handlers.api.v1_compatability import V1RedirectHandler, V1GoneHandler from server.handlers.api.alerts import APIAlertsHandler, APIAlertsStreamHandler from server.handlers.api.dxstats import APIDxStatsHandler from server.handlers.api.lookups import APILookupCallHandler, APILookupSIGRefHandler, APILookupGridHandler @@ -64,24 +65,31 @@ class WebServer: # API endpoints are always enabled api_routes = [ - (r"/api/v1/spots", APISpotsHandler, {"spots": self._spots, **handler_opts}), - (r"/api/v1/alerts", APIAlertsHandler, {"alerts": self._alerts, **handler_opts}), - (r"/api/v1/spots/stream", APISpotsStreamHandler, + (r"/api/v2/spots", APISpotsHandler, {"spots": self._spots, **handler_opts}), + (r"/api/v2/alerts", APIAlertsHandler, {"alerts": self._alerts, **handler_opts}), + (r"/api/v2/spots/stream", APISpotsStreamHandler, {"sse_spot_queues": self._sse_spot_queues, **handler_opts}), - (r"/api/v1/alerts/stream", APIAlertsStreamHandler, + (r"/api/v2/alerts/stream", APIAlertsStreamHandler, {"sse_alert_queues": self._sse_alert_queues, **handler_opts}), - (r"/api/v1/solar", APISolarConditionsHandler, {"solar_conditions": self._solar_conditions, **handler_opts}), - (r"/api/v1/dxstats", APIDxStatsHandler, {"spots": self._spots, **handler_opts}), - (r"/api/v1/options", APIOptionsHandler, + (r"/api/v2/solar", APISolarConditionsHandler, {"solar_conditions": self._solar_conditions, **handler_opts}), + (r"/api/v2/dxstats", APIDxStatsHandler, {"spots": self._spots, **handler_opts}), + (r"/api/v2/options", APIOptionsHandler, {"status_data": self._status_data, "spot_providers": self._spot_providers, **handler_opts}), - (r"/api/v1/status", APIStatusHandler, {"status_data": self._status_data, **handler_opts}), - (r"/api/v1/lookup/call", APILookupCallHandler, {**handler_opts}), - (r"/api/v1/lookup/sigref", APILookupSIGRefHandler, {**handler_opts}), - (r"/api/v1/lookup/grid", APILookupGridHandler, {**handler_opts}), - (r"/api/v1/spot", APISpotHandler, + (r"/api/v2/status", APIStatusHandler, {"status_data": self._status_data, **handler_opts}), + (r"/api/v2/lookup/call", APILookupCallHandler, {**handler_opts}), + (r"/api/v2/lookup/sigref", APILookupSIGRefHandler, {**handler_opts}), + (r"/api/v2/lookup/grid", APILookupGridHandler, {**handler_opts}), + (r"/api/v2/spot", APISpotHandler, {"spots": self._spots, "spot_providers": self._spot_providers, **handler_opts}), ] + # v1 API redirects. Most v1 enpoints are unchanged in v2, and get an HTTP 308 redirect to the v2 API. The ones + # that have the actual breaking changes get a bespoke handler. + v1_compat_routes = [ + (r"/api/v1/spot", V1GoneHandler), + (r"/api/v1/(.*)", V1RedirectHandler), + ] + # If in API-only mode, serve a basic homepage; in normal mode, serve the usual UI routes if self._api_only_mode: logging.info("API-only mode is enabled. Web UI will not be served.") @@ -109,7 +117,7 @@ class WebServer: (r"/(.*)", StaticFileHandler, {"path": os.path.join(_HERE, "../webassets")}) ] - app = tornado.web.Application(api_routes + ui_routes + misc_routes, + app = tornado.web.Application(api_routes + v1_compat_routes + ui_routes + misc_routes, template_path=os.path.join(_HERE, "../templates"), debug=False) app.listen(self._port) diff --git a/webassets/apidocs/openapi.yml b/webassets/apidocs/openapi.yml index 0ab168a..71c305c 100644 --- a/webassets/apidocs/openapi.yml +++ b/webassets/apidocs/openapi.yml @@ -14,12 +14,12 @@ info: Spothole's source code is located at https://git.ianrenton.com/ian/spothole and the README there provides setup instructions if you would like to run your own copy. A demonstration server of Spothole is located at https://spothole.app. ## Changelog - - ### 1.4 - * POST `/spot` now supports upstream submission to external providers such as POTA and SOTA via new `submit_upstream`, `upstream_provider`, and `upstream_credentials` request body fields. + ### 2.0 + + * POST `/spot` now supports upstream submission to external providers such as POTA and SOTA. The "add spot" API has a **breaking change** to enable this: instead of just posting the spot object itself as the JSON content of the POST, this has moved into a `spot` object within the structure. A new `handling` object alongside it contains the `submit_upstream`, `upstream_provider`, `upstream_credentials`, and `captcha_token` fields which control the server handling of the spot. * POST `/spot` now supports Google reCaptcha and (if the site owner has set it up) now requires `captcha_token` in order to successfully submit. (This is used to lock down the submit function and prevent submission via Spothole by bots or third-party clients.) - * GET `/options` now returns `spot_submit_providers`, a map of SIG names to the names of providers that support upstream spot submission for that SIG. + * GET `/options` now returns `spot_submit_providers`, a map of SIG names to the names of providers that support upstream spot submission for that SIG. (This allows clients to present the user with options of where a new spot can be sent to.) ### 1.3 @@ -42,10 +42,10 @@ info: license: name: The Unlicense url: https://unlicense.org/#the-unlicense - version: 1.4 + version: 2.0 servers: - - url: https://spothole.app/api/v1 + - url: https://spothole.app/api/v2 tags: - name: Spots @@ -356,10 +356,10 @@ paths: tags: - Spots summary: Add a spot - description: "Supply a new spot object, which will be added to the system. Optionally, set `submit_upstream` to true to forward the spot to an external provider such as POTA or SOTA. Check `spot_submit_providers` in the `/options` response to see which SIGs and providers support this. cURL example (local-only): `curl --request POST --header \"Content-Type: application/json\" --data '{\"dx_call\":\"M0TRT\",\"time\":1760019539, \"freq\":14200000, \"comment\":\"Test spot please ignore\", \"de_call\":\"M0TRT\"}' https://spothole.app/api/v1/spot`" + description: "Supply a spot submission object containing a `spot` sub-object (the spot data) and an optional `handling` sub-object (server-side instructions such as upstream submission). Check `spot_submit_providers` in the `/options` response to see which SIGs and providers support upstream submission. cURL example: `curl --request POST --header \"Content-Type: application/json\" --data '{\"spot\":{\"dx_call\":\"M0TRT\",\"time\":1760019539,\"freq\":14200000,\"comment\":\"Test spot please ignore\",\"de_call\":\"M0TRT\"}}' https://spothole.app/api/v2/spot`" operationId: spot requestBody: - description: The JSON spot object, plus optional upstream submission control fields + description: Object containing a "spot" sub-object with the spot data, and an optional "handling" sub-object with server-side instructions of what to do with it. required: true content: application/json: @@ -997,11 +997,18 @@ components: SpotSubmission: description: > - Request body for POST /spot. Contains all the fields of a Spot, plus optional - upstream submission control fields that are consumed by the server and never stored in the spot. - allOf: - - $ref: '#/components/schemas/Spot' - - type: object + Request body for POST /spot. Contains a "spot" sub-object with the spot data, and an optional + "handling" sub-object with server-side instructions consumed by Spothole. + type: object + required: + - spot + properties: + spot: + $ref: '#/components/schemas/Spot' + handling: + type: object + description: > + Optional server-side instructions for how to process this spot submission. properties: submit_upstream: type: boolean @@ -1021,7 +1028,7 @@ components: type: object description: > Provider-specific credentials required to authenticate the upstream submission. - The required keys depend on the provider . Credentials are used only for the upstream + The required keys depend on the provider. Credentials are used only for the upstream call and are never stored by Spothole. additionalProperties: type: string @@ -1032,8 +1039,7 @@ components: type: string description: > A Google reCAPTCHA v2 response token. Required when submitting upstream if the - server has reCAPTCHA configured (i.e. `submit_upstream` is true and the server - operator has set up reCAPTCHA keys). Obtain the token by completing the reCAPTCHA + server has reCAPTCHA configured. Obtain the token by completing the reCAPTCHA widget rendered on the Add Spot page. example: "03AFY_a8Xq..." @@ -1678,7 +1684,7 @@ components: example: "EU" max_spot_age: type: integer - description: The maximum age, in seconds, of any spot before it will be deleted by the system. When querying the /api/v1/spots endpoint and providing a "max_age" or "since" parameter, there is no point providing a number larger than this, because the system drops all spots older than this. + description: The maximum age, in seconds, of any spot before it will be deleted by the system. When querying the /api/v2/spots endpoint and providing a "max_age" or "since" parameter, there is no point providing a number larger than this, because the system drops all spots older than this. example: 3600 spot_allowed: type: boolean diff --git a/webassets/js/add-spot.js b/webassets/js/add-spot.js index 12d3586..847afaa 100644 --- a/webassets/js/add-spot.js +++ b/webassets/js/add-spot.js @@ -29,7 +29,7 @@ const PROVIDER_CREDENTIAL_SCHEMAS = { // Load server options. Once a successful callback is made from this, we can populate the choice boxes in the form and load // any saved values from local storage. function loadOptions() { - $.getJSON('/api/v1/options', function (jsonData) { + $.getJSON('/api/v2/options', function (jsonData) { // Store options options = jsonData; @@ -203,6 +203,7 @@ function addSpot() { const comment = $("#comment").val(); const de = $("#de-call").val().toUpperCase(); + // Prepare the spot object for the server const spot = {}; if (dx !== "") { spot["dx_call"] = dx; @@ -240,6 +241,19 @@ function addSpot() { } spot["time"] = moment.utc().valueOf() / 1000.0; + // Prepare "handling" structure to tell the server what to do with this spot + const handling = {}; + + // Add CAPTCHA token if reCAPTCHA is loaded + if (window._recaptchaWidgetId !== undefined) { + const token = grecaptcha.getResponse(window._recaptchaWidgetId); + if (!token) { + showAddSpotError("Please complete the CAPTCHA to submit upstream."); + return; + } + handling["captcha_token"] = token; + } + // Upstream submission const submitUpstream = $("#submit-upstream").is(":checked"); const upstreamProviderName = getSelectedUpstreamProvider(); @@ -261,24 +275,13 @@ function addSpot() { return; } - const creds = loadCredentials(upstreamProviderName); - spot["submit_upstream"] = true; - spot["upstream_provider"] = upstreamProviderName; - spot["upstream_credentials"] = creds; - - // Add CAPTCHA token if reCAPTCHA is loaded - if (window._recaptchaWidgetId !== undefined) { - const token = grecaptcha.getResponse(window._recaptchaWidgetId); - if (!token) { - showAddSpotError("Please complete the CAPTCHA to submit upstream."); - return; - } - spot["captcha_token"] = token; - } + handling["submit_upstream"] = true; + handling["upstream_provider"] = upstreamProviderName; + handling["upstream_credentials"] = loadCredentials(upstreamProviderName); } - $.ajax("/api/v1/spot", { - data: JSON.stringify(spot), + $.ajax("/api/v2/spot", { + data: JSON.stringify({spot, handling}), contentType: 'application/json', type: 'POST', timeout: 10000, diff --git a/webassets/js/alerts.js b/webassets/js/alerts.js index 35c127c..d9f3970 100644 --- a/webassets/js/alerts.js +++ b/webassets/js/alerts.js @@ -6,7 +6,7 @@ let alerts = []; // Load alerts and populate the table. function loadAlerts() { - $.getJSON('/api/v1/alerts' + buildQueryString(false), function (jsonData) { + $.getJSON('/api/v2/alerts' + buildQueryString(false), function (jsonData) { // Store last updated time lastUpdateTime = moment.utc(); updateRefreshDisplay(); @@ -280,7 +280,7 @@ function addAlertRowsToTable(tbody, alerts) { // Load server options. Once a successful callback is made from this, we then query alerts. function loadOptions() { - $.getJSON('/api/v1/options', function (jsonData) { + $.getJSON('/api/v2/options', function (jsonData) { // Store options options = jsonData; diff --git a/webassets/js/bands.js b/webassets/js/bands.js index dd42157..39229dd 100644 --- a/webassets/js/bands.js +++ b/webassets/js/bands.js @@ -12,7 +12,7 @@ BAND_COLUMN_SPOT_DIV_HEIGHT_PX = BAND_COLUMN_FONT_SIZE * 1.6; // Load spots and populate the bands display. function loadSpots() { - $.getJSON('/api/v1/spots' + buildQueryString(false), function (jsonData) { + $.getJSON('/api/v2/spots' + buildQueryString(false), function (jsonData) { // Store last updated time lastUpdateTime = moment.utc(); updateRefreshDisplay(); @@ -229,7 +229,7 @@ function removeDuplicatesForBandPanel(spotList) { // Load server options. Once a successful callback is made from this, we then query spots and set up the timer to query // spots repeatedly. function loadOptions() { - $.getJSON('/api/v1/options', function (jsonData) { + $.getJSON('/api/v2/options', function (jsonData) { // Store options options = jsonData; diff --git a/webassets/js/conditions.js b/webassets/js/conditions.js index fef0a77..79c21a7 100644 --- a/webassets/js/conditions.js +++ b/webassets/js/conditions.js @@ -10,7 +10,7 @@ let ionosondeChart = null; // Load solar conditions function loadSolarConditions() { - $.getJSON('/api/v1/solar', function (jsonData) { + $.getJSON('/api/v2/solar', function (jsonData) { // HF @@ -660,7 +660,7 @@ function dxStatsContientChanged() { // Fetch DX stats from the API and render function loadDxStats() { - $.getJSON('/api/v1/dxstats', function (jsonData) { + $.getJSON('/api/v2/dxstats', function (jsonData) { dxStatsData = jsonData; renderDxStats(); }); diff --git a/webassets/js/map.js b/webassets/js/map.js index da35d78..889f3ef 100644 --- a/webassets/js/map.js +++ b/webassets/js/map.js @@ -28,7 +28,7 @@ let firstLoad = true; // Load spots and populate the map. function loadSpots() { - $.getJSON('/api/v1/spots' + buildQueryString(true), function (jsonData) { + $.getJSON('/api/v2/spots' + buildQueryString(true), function (jsonData) { // Store data spots = jsonData; // Update map @@ -194,7 +194,7 @@ function getTooltipText(s) { // Load server options. Once a successful callback is made from this, we then query spots and set up the timer to query // spots repeatedly. function loadOptions() { - $.getJSON('/api/v1/options', function (jsonData) { + $.getJSON('/api/v2/options', function (jsonData) { // Store options options = jsonData; diff --git a/webassets/js/spots.js b/webassets/js/spots.js index 9b6418e..18cb17e 100644 --- a/webassets/js/spots.js +++ b/webassets/js/spots.js @@ -20,7 +20,7 @@ function loadSpots() { } // Make the new query - $.getJSON('/api/v1/spots' + buildQueryString(false), function (jsonData) { + $.getJSON('/api/v2/spots' + buildQueryString(false), function (jsonData) { // Store data spots = jsonData; // Update table @@ -39,7 +39,7 @@ function startSSEConnection() { if (evtSource != null) { evtSource.close(); } - evtSource = new EventSource('/api/v1/spots/stream' + buildQueryString(true)); + evtSource = new EventSource('/api/v2/spots/stream' + buildQueryString(true)); evtSource.onmessage = function (event) { // Get the new spot @@ -418,7 +418,7 @@ function createNewTableRowsForSpot(s, highlightNew) { // Load server options. Once a successful callback is made from this, we then query spots and set up the timer to query // spots repeatedly. function loadOptions() { - $.getJSON('/api/v1/options', function (jsonData) { + $.getJSON('/api/v2/options', function (jsonData) { // Store options options = jsonData; diff --git a/webassets/js/status.js b/webassets/js/status.js index 33f5f82..c6377ab 100644 --- a/webassets/js/status.js +++ b/webassets/js/status.js @@ -1,6 +1,6 @@ // Load server status function loadStatus() { - $.getJSON('/api/v1/status', function (jsonData) { + $.getJSON('/api/v2/status', function (jsonData) { $("#software-version").text(jsonData["software-version"]); $("#server-owner-callsign").text(jsonData["server-owner-callsign"]); $("#up-since").text(moment().subtract(jsonData["uptime"], 'seconds').fromNow());