nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

commit d170d90841aaf52d29fb5e02024f67c6dac2912a
parent 0ef1bd850dc8b6794133d91f541d7c5296fb5450
Author: Sergej Orlov <wladimirych@gmail.com>
Date:   Sun,  5 Jan 2025 22:27:28 +0100

tracks: load Strava activities using short URL

Fixes #1254

Diffstat:
Msrc/layers.js | 2+-
Msrc/lib/CORSProxy/index.js | 15++++++++++++++-
Msrc/lib/leaflet.control.search/providers/links.js | 2+-
Msrc/lib/leaflet.control.search/providers/mapycz/index.js | 2+-
Msrc/lib/leaflet.control.track-list/lib/services/etomesto.js | 2+-
Msrc/lib/leaflet.control.track-list/lib/services/garmin.js | 2+-
Msrc/lib/leaflet.control.track-list/lib/services/index.js | 3++-
Msrc/lib/leaflet.control.track-list/lib/services/osm.js | 2+-
Msrc/lib/leaflet.control.track-list/lib/services/simpleService.js | 2+-
Msrc/lib/leaflet.control.track-list/lib/services/sportstracker.js | 2+-
Msrc/lib/leaflet.control.track-list/lib/services/strava.js | 34++++++++++++++++++++++++++++++++--
Msrc/lib/leaflet.control.track-list/lib/services/tracedetrail.js | 2+-
Msrc/lib/leaflet.control.track-list/lib/services/wikiloc.js | 2+-
Msrc/lib/leaflet.layer.TileLayer.cutline/index.js | 2+-
Msrc/lib/leaflet.layer.rasterize/TileLayer.js | 2+-
Mtest/test_track_load.js | 3+++
Atest/track_load_data/testcases/strava_short_url_deleted.json | 4++++
Atest/track_load_data/testcases/strava_short_url_not_exists.json | 4++++
Atest/track_load_data/testcases/strava_short_url_private.json | 4++++
Mtest/track_load_data/testcases/strava_with_title.json | 3++-
Mtest/track_load_data/testcases/strava_without_title.json | 5++++-
21 files changed, 81 insertions(+), 18 deletions(-)

diff --git a/src/layers.js b/src/layers.js @@ -8,7 +8,7 @@ import '~/lib/leaflet.layer.westraPasses'; import '~/lib/leaflet.layer.wikimapia'; import {GeocachingSu} from '~/lib/leaflet.layer.geocaching-su'; import {RetinaTileLayer} from '~/lib/leaflet.layer.RetinaTileLayer'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; import '~/lib/leaflet.layer.TileLayer.cutline'; import {getCutline} from '~/lib/layers-cutlines'; import {LayerCutlineOverview} from '~/lib/leaflet.layer.LayerCutlineOverview'; diff --git a/src/lib/CORSProxy/index.js b/src/lib/CORSProxy/index.js @@ -1,6 +1,17 @@ import config from '~/config'; -export default function urlViaCorsProxy(url) { +function corsProxyOriginalUrl(url) { + if (!url.startsWith(config.CORSProxyUrl)) { + throw new Error('URL is not via CORS proxy'); + } + url = url.slice(config.CORSProxyUrl.length); + if (!url.match(/^https?\//u)) { + throw new Error('Invalid URL via CORS proxy'); + } + return url.replace('/', '://'); +} + +function urlViaCorsProxy(url) { for (let pattern of config.urlsBypassCORSProxy) { if (pattern.test(url)) { return url; @@ -8,3 +19,5 @@ export default function urlViaCorsProxy(url) { } return config.CORSProxyUrl + url.replace(/^(https?):\/\//u, '$1/'); } + +export {urlViaCorsProxy, corsProxyOriginalUrl}; diff --git a/src/lib/leaflet.control.search/providers/links.js b/src/lib/leaflet.control.search/providers/links.js @@ -1,6 +1,6 @@ import L from 'leaflet'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; import {fetch} from '~/lib/xhr-promise'; const MAX_ZOOM = 18; diff --git a/src/lib/leaflet.control.search/providers/mapycz/index.js b/src/lib/leaflet.control.search/providers/mapycz/index.js @@ -1,6 +1,6 @@ import L from 'leaflet'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; import * as logging from '~/lib/logging'; import {fetch} from '~/lib/xhr-promise'; diff --git a/src/lib/leaflet.control.track-list/lib/services/etomesto.js b/src/lib/leaflet.control.track-list/lib/services/etomesto.js @@ -1,4 +1,4 @@ -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; import BaseService from './baseService'; import parseGpx from '../parsers/gpx'; diff --git a/src/lib/leaflet.control.track-list/lib/services/garmin.js b/src/lib/leaflet.control.track-list/lib/services/garmin.js @@ -1,5 +1,5 @@ import BaseService from './baseService'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; class GarminBase extends BaseService { isOurUrl() { diff --git a/src/lib/leaflet.control.track-list/lib/services/index.js b/src/lib/leaflet.control.track-list/lib/services/index.js @@ -1,7 +1,7 @@ import SimpleService from './simpleService'; import Etomesto from './etomesto'; import Osm from './osm'; -import Strava from './strava'; +import {Strava, StravaShortUrl} from './strava'; import Tracedetrail from './tracedetrail'; import {YandexRuler} from './yandex'; import {NakarteTrack, NakarteUrl} from './nakarte'; @@ -15,6 +15,7 @@ const services = [ NakarteUrl, Etomesto, Osm, + StravaShortUrl, Strava, Tracedetrail, GarminActivity, diff --git a/src/lib/leaflet.control.track-list/lib/services/osm.js b/src/lib/leaflet.control.track-list/lib/services/osm.js @@ -1,4 +1,4 @@ -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; import BaseService from './baseService'; import parseGpx from '../parsers/gpx'; diff --git a/src/lib/leaflet.control.track-list/lib/services/simpleService.js b/src/lib/leaflet.control.track-list/lib/services/simpleService.js @@ -1,6 +1,6 @@ import BaseService from './baseService'; import parseGeoFile from '../parseGeoFile'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; class SimpleService extends BaseService { isOurUrl() { diff --git a/src/lib/leaflet.control.track-list/lib/services/sportstracker.js b/src/lib/leaflet.control.track-list/lib/services/sportstracker.js @@ -1,5 +1,5 @@ import BaseService from './baseService'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; import utf8 from 'utf8'; class SportsTrackerBase extends BaseService { diff --git a/src/lib/leaflet.control.track-list/lib/services/strava.js b/src/lib/leaflet.control.track-list/lib/services/strava.js @@ -1,5 +1,5 @@ import BaseService from './baseService'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {corsProxyOriginalUrl, urlViaCorsProxy} from '~/lib/CORSProxy'; class Strava extends BaseService { urlRe = /^https?:\/\/(?:.+\.)?strava\.com\/activities\/(\d+)/u; @@ -69,4 +69,34 @@ class Strava extends BaseService { } } -export default Strava; +class StravaShortUrl extends BaseService { + urlRe = /^https:\/\/strava.app.link\/([A-Za-z0-9]+)/u; + + isOurUrl() { + return this.urlRe.test(this.origUrl); + } + + requestOptions() { + return [ + { + url: urlViaCorsProxy(this.origUrl), + options: {isResponseSuccess: (response) => [200, 404].includes(response.status)}, + }, + ]; + } + + parseResponse(responses) { + const response = responses[0]; + const url = corsProxyOriginalUrl(response.responseURL); + if (response.status === 404) { + return [{error: 'Requested Strava activity was deleted'}]; + } + const strava = new Strava(url); + if (!strava.isOurUrl()) { + return [{error: 'Bad short link for Strava activity or activity is marked as private'}]; + } + return strava.geoData(); + } +} + +export {Strava, StravaShortUrl}; diff --git a/src/lib/leaflet.control.track-list/lib/services/tracedetrail.js b/src/lib/leaflet.control.track-list/lib/services/tracedetrail.js @@ -1,5 +1,5 @@ import BaseService from './baseService'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; import L from 'leaflet'; class Tracedetrail extends BaseService { diff --git a/src/lib/leaflet.control.track-list/lib/services/wikiloc.js b/src/lib/leaflet.control.track-list/lib/services/wikiloc.js @@ -1,5 +1,5 @@ import BaseService from './baseService'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; import {stringToArrayBuffer} from '~/lib/binary-strings'; import twkb from 'twkb'; diff --git a/src/lib/leaflet.layer.TileLayer.cutline/index.js b/src/lib/leaflet.layer.TileLayer.cutline/index.js @@ -1,6 +1,6 @@ import L from 'leaflet'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; const origCreateTile = L.TileLayer.prototype.createTile; const origIsValidTile = L.TileLayer.prototype._isValidTile; diff --git a/src/lib/leaflet.layer.rasterize/TileLayer.js b/src/lib/leaflet.layer.rasterize/TileLayer.js @@ -1,5 +1,5 @@ import L from 'leaflet'; -import urlViaCorsProxy from '~/lib/CORSProxy'; +import {urlViaCorsProxy} from '~/lib/CORSProxy'; import {imgFromDataString} from './imgFromDataString'; function noop() { diff --git a/test/test_track_load.js b/test/test_track_load.js @@ -41,6 +41,9 @@ suite('Load tracks from services'); 'strava_without_title', 'strava_private', 'strava_not_exists', + 'strava_short_url_private', + 'strava_short_url_not_exists', + 'strava_short_url_deleted', 'garmin_connect_activity_with_title', 'garmin_connect_activity_without_title', 'garmin_connect_activity_private', diff --git a/test/track_load_data/testcases/strava_short_url_deleted.json b/test/track_load_data/testcases/strava_short_url_deleted.json @@ -0,0 +1,4 @@ +{ + "query": ["https://strava.app.link/Sq2CMRdTUPb"], + "geodata": [{"error": "Requested Strava activity was deleted"}] +} diff --git a/test/track_load_data/testcases/strava_short_url_not_exists.json b/test/track_load_data/testcases/strava_short_url_not_exists.json @@ -0,0 +1,4 @@ +{ + "query": ["https://strava.app.link/Sq2CMRdTU"], + "geodata": [{"error": "Bad short link for Strava activity or activity is marked as private"}] +} diff --git a/test/track_load_data/testcases/strava_short_url_private.json b/test/track_load_data/testcases/strava_short_url_private.json @@ -0,0 +1,4 @@ +{ + "query": ["https://strava.app.link/hRestZOPUPb"], + "geodata": [{"error": "Bad short link for Strava activity or activity is marked as private"}] +} diff --git a/test/track_load_data/testcases/strava_with_title.json b/test/track_load_data/testcases/strava_with_title.json @@ -4,7 +4,8 @@ "https://www.strava.com/activities/3873704997?a=1", "https://www.strava.com/activities/3873704997/", "https://strava.com/activities/3873704997", - "http://www.strava.com/activities/3873704997" + "http://www.strava.com/activities/3873704997", + "https://strava.app.link/5aLJ0QIPUPb" ], "geodata": [ { diff --git a/test/track_load_data/testcases/strava_without_title.json b/test/track_load_data/testcases/strava_without_title.json @@ -1,5 +1,8 @@ { - "query": ["https://www.strava.com/activities/3873768093"], + "query": [ + "https://www.strava.com/activities/3873768093", + "https://strava.app.link/a2LqU4JPUPb" + ], "geodata": [ { "name": "я угу - Дневной забег 5 July 2014",