kopia lustrzana https://github.com/manuelkasper/sotlas-frontend
Obtain MapTiler key from server, providing SSO or Captcha (Turnstile) information for authentication
rodzic
23606b0867
commit
67d2c6f074
1
.env
1
.env
|
@ -3,7 +3,6 @@ VITE_WSS_URL="wss://sotl.as/api"
|
|||
VITE_PHOTOS_URL="https://photos.sotl.as"
|
||||
VITE_PHOTOS_ORIGINAL_URL="https://sotlas-photos.s3.eu-central-003.backblazeb2.com/original"
|
||||
VITE_ELEVATION_API_URL="https://elevation.sotl.as/api"
|
||||
VITE_MAPTILER_KEY="ngpPmAguBVHe7Dcxki1g"
|
||||
VITE_GEONAMES_USERNAME="neon1"
|
||||
VITE_WINDY_API_KEY="FIHFGWMrA0iF5Wz4fnBIR8Sb0GRUUeQY"
|
||||
VITE_AZ_URL="https://az.sotl.as"
|
||||
|
|
|
@ -32,8 +32,6 @@
|
|||
}
|
||||
</script>
|
||||
<div id="app"></div>
|
||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||
<div class="cf-turnstile" data-sitekey="%VITE_TURNSTILE_SITE_KEY%"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
"@fortawesome/pro-regular-svg-icons": "^5.15.4",
|
||||
"@fortawesome/pro-solid-svg-icons": "^5.15.4",
|
||||
"@fortawesome/vue-fontawesome": "^2.0.10",
|
||||
"@gaviti/vue-turnstile": "^0.6.5",
|
||||
"@mapbox/mapbox-gl-draw": "github:manuelkasper/mapbox-gl-draw#sotlas2",
|
||||
"@maptiler/sdk": "^2.0.3",
|
||||
"@tmcw/togeojson": "^3.2.0",
|
||||
|
@ -35,7 +36,7 @@
|
|||
"proj4": "^2.7.2",
|
||||
"vue": "^2.7.16",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-filepond": "^6.0.3",
|
||||
"vue-filepond": "^6.0.2",
|
||||
"vue-infinite-loading": "^2.4.5",
|
||||
"vue-lazy-youtube-video": "^2.3.0",
|
||||
"vue-mapbox": "github:manuelkasper/vue-mapbox#10cb772",
|
||||
|
@ -775,6 +776,18 @@
|
|||
"vue": "~2"
|
||||
}
|
||||
},
|
||||
"node_modules/@gaviti/vue-turnstile": {
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@gaviti/vue-turnstile/-/vue-turnstile-0.6.5.tgz",
|
||||
"integrity": "sha512-kS1SVFzDAFqiqrvEOPNQIRWfCi6tv9w/2aNBRdUa217PXjjSt35sofMqsz5LiU4xqyvnBMkp1f/aNUhNa8GxFg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vue": "^2.7.16"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >= 16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
|
@ -1725,9 +1738,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
||||
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/data-view-buffer": {
|
||||
"version": "1.0.1",
|
||||
|
@ -4812,9 +4826,10 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/vue-filepond": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-filepond/-/vue-filepond-6.0.3.tgz",
|
||||
"integrity": "sha512-m0wArAdpgzOOs19bWA6zzYlHAb2aK+igPoKPZGrzpgKiiELPKW7XZ2OBDXzk7rhpFLkedujVrMqwjPyZfmQTTQ==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-filepond/-/vue-filepond-6.0.2.tgz",
|
||||
"integrity": "sha512-HE1TvV2LrKMU3pFeIBbR9Uf6cryNLVnnl35uSL+HLsZ6rfZ3C1alfre3UcaSoKTVuxNsn41Ce4IR528UwdxZfw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"filepond": ">=4.7.4 < 5.x",
|
||||
"vue": ">=2.6.0 < 3.x"
|
||||
|
@ -5369,6 +5384,14 @@
|
|||
"integrity": "sha512-OTETSXz+3ygD2OK2/vy82cmUBpuJqeOAg4gfnnv+f2Rir1tDIhQg026Q3NQxznq83ZLz8iNqGG9XJm26inpDeg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@gaviti/vue-turnstile": {
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@gaviti/vue-turnstile/-/vue-turnstile-0.6.5.tgz",
|
||||
"integrity": "sha512-kS1SVFzDAFqiqrvEOPNQIRWfCi6tv9w/2aNBRdUa217PXjjSt35sofMqsz5LiU4xqyvnBMkp1f/aNUhNa8GxFg==",
|
||||
"requires": {
|
||||
"vue": "^2.7.16"
|
||||
}
|
||||
},
|
||||
"@humanwhocodes/config-array": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
|
@ -6061,9 +6084,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"csstype": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
||||
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"data-view-buffer": {
|
||||
"version": "1.0.1",
|
||||
|
@ -8229,9 +8252,9 @@
|
|||
}
|
||||
},
|
||||
"vue-filepond": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-filepond/-/vue-filepond-6.0.3.tgz",
|
||||
"integrity": "sha512-m0wArAdpgzOOs19bWA6zzYlHAb2aK+igPoKPZGrzpgKiiELPKW7XZ2OBDXzk7rhpFLkedujVrMqwjPyZfmQTTQ==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-filepond/-/vue-filepond-6.0.2.tgz",
|
||||
"integrity": "sha512-HE1TvV2LrKMU3pFeIBbR9Uf6cryNLVnnl35uSL+HLsZ6rfZ3C1alfre3UcaSoKTVuxNsn41Ce4IR528UwdxZfw==",
|
||||
"requires": {}
|
||||
},
|
||||
"vue-infinite-loading": {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
"@fortawesome/pro-regular-svg-icons": "^5.15.4",
|
||||
"@fortawesome/pro-solid-svg-icons": "^5.15.4",
|
||||
"@fortawesome/vue-fontawesome": "^2.0.10",
|
||||
"@gaviti/vue-turnstile": "^0.6.5",
|
||||
"@mapbox/mapbox-gl-draw": "github:manuelkasper/mapbox-gl-draw#sotlas2",
|
||||
"@maptiler/sdk": "^2.0.3",
|
||||
"@tmcw/togeojson": "^3.2.0",
|
||||
|
@ -39,7 +40,7 @@
|
|||
"proj4": "^2.7.2",
|
||||
"vue": "^2.7.16",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-filepond": "^6.0.3",
|
||||
"vue-filepond": "^6.0.2",
|
||||
"vue-infinite-loading": "^2.4.5",
|
||||
"vue-lazy-youtube-video": "^2.3.0",
|
||||
"vue-mapbox": "github:manuelkasper/vue-mapbox#10cb772",
|
||||
|
|
16
src/App.vue
16
src/App.vue
|
@ -4,14 +4,28 @@
|
|||
<keep-alive include="Map">
|
||||
<router-view />
|
||||
</keep-alive>
|
||||
<vue-turnstile v-if="!authenticated" :site-key="siteKey" @verified="turnstileVerified" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NavBar from './components/NavBar.vue'
|
||||
import VueTurnstile from '@gaviti/vue-turnstile'
|
||||
import utils from './mixins/utils.js'
|
||||
|
||||
export default {
|
||||
components: { NavBar }
|
||||
mixins: [utils],
|
||||
components: { NavBar, VueTurnstile },
|
||||
computed: {
|
||||
siteKey () {
|
||||
return import.meta.env.VITE_TURNSTILE_SITE_KEY
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
turnstileVerified(token) {
|
||||
this.$store.commit('setTurnstileToken', token)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ export default {
|
|||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.homeQth === null) {
|
||||
if (this.homeQth === null && this.authenticated) {
|
||||
this.$keycloak.updateToken(60)
|
||||
.then(() => {
|
||||
this.$keycloak.loadUserProfile()
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<template>
|
||||
<div class="map-key-failed-info">
|
||||
<div class="info-card">
|
||||
<font-awesome-icon :icon="['far', 'exclamation-circle']" class="faicon" />
|
||||
<h3 class="title">Anti-bot verification failed</h3>
|
||||
<p class="messagex">The automatic verification that you are not a bot failed. Please log in with your SOTA
|
||||
account to view the map.</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'MapKeyFailedInfo'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.map-key-failed-info {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f7f7f7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.info-card .faicon {
|
||||
color: #f59e0b;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
margin: 0 0 0.75rem 0;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.messagex {
|
||||
font-size: 1rem;
|
||||
color: #6b7280;
|
||||
margin: 0 0 0.5rem 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
|
@ -1,19 +1,28 @@
|
|||
<template>
|
||||
<MglMap v-if="(mapCenter || bounds) && mapStyle" :apiKey="mapApiKey" :key="mapKey" :mapStyle="mapStyle" :bounds="bounds" :fitBoundsOptions="fitBoundsOptions" :center="mapCenter" :zoom="12.5" :attributionControl="false" @load="onMapLoaded" @click="onMapClicked" @contextmenu="onMapRightClicked" @idle="onMapIdle">
|
||||
<MglGeolocateControl v-if="!$mq.mobile || isEnlarged" :positionOptions="{ enableHighAccuracy: true }" :fitBoundsOptions="{ maxZoom: 12.5 }" :trackUserLocation="true" position="top-right" />
|
||||
<MglNavigationControl v-if="!$mq.mobile" position="top-right" :showCompass="false" />
|
||||
<MglScaleControl v-if="!$mq.mobile || isEnlarged" position="bottom-left" :unit="mapUnits" />
|
||||
<div v-if="canEnlarge" class="maplibregl-ctrl-top-left">
|
||||
<MapEnlargeControl :isEnlarged="isEnlarged" @enlarge="$emit('enlarge')" />
|
||||
</div>
|
||||
<MglAttributionControl :compact="$mq.mobile" position="bottom-right" />
|
||||
<div>
|
||||
<MglMap v-if="(mapCenter || bounds) && mapStyle" :apiKey="mapTilerApiKey" :key="mapKey"
|
||||
:mapStyle="mapStyle" :bounds="bounds" :fitBoundsOptions="fitBoundsOptions" :center="mapCenter" :zoom="12.5"
|
||||
:attributionControl="false" @load="onMapLoaded" @click="onMapClicked" @contextmenu="onMapRightClicked"
|
||||
@idle="onMapIdle">
|
||||
<MglGeolocateControl v-if="!$mq.mobile || isEnlarged" :positionOptions="{ enableHighAccuracy: true }"
|
||||
:fitBoundsOptions="{ maxZoom: 12.5 }" :trackUserLocation="true" position="top-right" />
|
||||
<MglNavigationControl v-if="!$mq.mobile" position="top-right" :showCompass="false" />
|
||||
<MglScaleControl v-if="!$mq.mobile || isEnlarged" position="bottom-left" :unit="mapUnits" />
|
||||
<div v-if="canEnlarge" class="maplibregl-ctrl-top-left">
|
||||
<MapEnlargeControl :isEnlarged="isEnlarged" @enlarge="$emit('enlarge')" />
|
||||
</div>
|
||||
<MglAttributionControl :compact="$mq.mobile" position="bottom-right" />
|
||||
|
||||
<MapRoute v-for="route in routes" :key="route.id" :route="route" />
|
||||
<MapPhoto v-for="photo in mapPhotos" :key="photo.filename" :summit="summit" :photo="photo" @photoClicked="photo => $emit('photoClicked', photo)" />
|
||||
<MapInfoPopup v-if="infoCoordinates !== null" :coordinates="infoCoordinates" @close="infoCoordinates = null" />
|
||||
<MapWebcams v-if="mapOptions.webcams" size="is-small" />
|
||||
<div v-if="zoomWarningVisible" class="zoom-warning">Zoom in to see all activations</div>
|
||||
</MglMap>
|
||||
<MapRoute v-for="route in routes" :key="route.id" :route="route" />
|
||||
<MapPhoto v-for="photo in mapPhotos" :key="photo.filename" :summit="summit" :photo="photo"
|
||||
@photoClicked="photo => $emit('photoClicked', photo)" />
|
||||
<MapInfoPopup v-if="infoCoordinates !== null" :coordinates="infoCoordinates" @close="infoCoordinates = null" />
|
||||
<MapWebcams v-if="mapOptions.webcams" size="is-small" />
|
||||
<div v-if="zoomWarningVisible" class="zoom-warning">Zoom in to see all activations</div>
|
||||
</MglMap>
|
||||
<MapKeyFailedInfo v-if="mapTilerApiKeyFailed" />
|
||||
<b-loading class="mini-map-loading" :is-full-page="false" :active="!mapStyle && !mapTilerApiKeyFailed" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -27,6 +36,7 @@ import mapstyle from '../mixins/mapstyle.js'
|
|||
import utils from '../mixins/utils.js'
|
||||
import longtouch from '../mixins/longtouch.js'
|
||||
import reportMapSession from '../mapsession.js'
|
||||
import MapKeyFailedInfo from './MapKeyFailedInfo.vue'
|
||||
|
||||
export default {
|
||||
name: 'MiniMap',
|
||||
|
@ -45,7 +55,7 @@ export default {
|
|||
overviewMap: Boolean
|
||||
},
|
||||
components: {
|
||||
MglMap, MglGeolocateControl, MglNavigationControl, MapEnlargeControl, MglScaleControl, MglAttributionControl, MapRoute, MapPhoto, MapInfoPopup, MapWebcams
|
||||
MglMap, MglGeolocateControl, MglNavigationControl, MapEnlargeControl, MglScaleControl, MglAttributionControl, MapRoute, MapPhoto, MapInfoPopup, MapWebcams, MapKeyFailedInfo
|
||||
},
|
||||
mixins: [utils, mapstyle, longtouch],
|
||||
watch: {
|
||||
|
@ -261,4 +271,9 @@ export default {
|
|||
text-align: center;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.mini-map-loading {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -33,6 +33,16 @@ export default {
|
|||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
},
|
||||
loadMapTilerApiKey (turnstileToken = null) {
|
||||
let params = {}
|
||||
if (turnstileToken) {
|
||||
params.token = turnstileToken
|
||||
}
|
||||
return this.axiosAuthOptional.get(import.meta.env.VITE_API_URL + '/mapkey/get', { params })
|
||||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import api from './api.js'
|
||||
import basemapatStyle from '../assets/basemapat.json'
|
||||
import caltopoStyle from '../assets/caltopo.json'
|
||||
import norkartStyle from '../assets/norkart.json'
|
||||
|
@ -8,11 +9,17 @@ import toposvalbardStyle from '../assets/toposvalbard.json'
|
|||
import partialSnowcoverDots from '../assets/partial-snowcover-dots.png'
|
||||
|
||||
export default {
|
||||
mixins: [api],
|
||||
mounted () {
|
||||
this.initialMapOptions = { ...this.mapOptions }
|
||||
this.updateMapTilerApiKey()
|
||||
},
|
||||
computed: {
|
||||
mapStyle () {
|
||||
if (!this.mapTilerApiKey) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (this.mapType === 'maptiler_outdoor') {
|
||||
if (this.$store.state.altitudeUnits === 'ft') {
|
||||
return 'dc9edd90-1320-4fa4-98ba-ad2d4efe5998'
|
||||
|
@ -43,10 +50,10 @@ export default {
|
|||
// Patch MapTiler key
|
||||
Object.values(style.sources).forEach(source => {
|
||||
if (source.url) {
|
||||
source.url = source.url.replace('{key}', import.meta.env.VITE_MAPTILER_KEY)
|
||||
source.url = source.url.replace('{key}', this.mapTilerApiKey)
|
||||
}
|
||||
})
|
||||
style.glyphs = style.glyphs.replace('{key}', import.meta.env.VITE_MAPTILER_KEY)
|
||||
style.glyphs = style.glyphs.replace('{key}', this.mapTilerApiKey)
|
||||
|
||||
// Patch units
|
||||
if (this.$store.state.altitudeUnits === 'ft') {
|
||||
|
@ -68,8 +75,8 @@ export default {
|
|||
}
|
||||
return mapType
|
||||
},
|
||||
mapApiKey () {
|
||||
return import.meta.env.VITE_MAPTILER_KEY
|
||||
mapTilerApiKey () {
|
||||
return this.$store.state.mapTilerApiKey
|
||||
},
|
||||
mapUnits () {
|
||||
if (this.$store.state.altitudeUnits === 'ft') {
|
||||
|
@ -80,6 +87,26 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
updateMapTilerApiKey () {
|
||||
if (this.$store.state.mapTilerApiKey) {
|
||||
return
|
||||
}
|
||||
|
||||
// If we are logged in via SSO, then there's no need for Turnstile
|
||||
if ((this.$keycloak && this.$keycloak.authenticated) || this.$store.state.turnstileToken) {
|
||||
this.loadMapTilerApiKey(this.$store.state.turnstileToken)
|
||||
.then(response => {
|
||||
this.$store.commit('setMapTilerApiKey', response.mapTilerApiKey)
|
||||
if (this.$store.state.turnstileToken) {
|
||||
this.$store.commit('setTurnstileToken', null)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
this.mapTilerApiKeyFailed = true
|
||||
})
|
||||
}
|
||||
},
|
||||
updateLayers (map) {
|
||||
if (!map) {
|
||||
return
|
||||
|
@ -168,6 +195,13 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$store.state.turnstileToken': {
|
||||
handler () {
|
||||
this.updateMapTilerApiKey()
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
mapTypes: {
|
||||
|
@ -230,7 +264,9 @@ export default {
|
|||
style: norkartStyle
|
||||
}
|
||||
},
|
||||
initialMapOptions: null
|
||||
initialMapOptions: null,
|
||||
mapTilerApiKey: null,
|
||||
mapTilerApiKeyFailed: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,33 +3,32 @@ import axios from 'axios'
|
|||
export default {
|
||||
computed: {
|
||||
axiosAuth () {
|
||||
let instance = axios.create()
|
||||
instance.interceptors.request.use(config => new Promise((resolve, reject) => {
|
||||
if (!this.$keycloak || !this.$keycloak.authenticated) {
|
||||
reject(new Error('not logged in'))
|
||||
} else {
|
||||
this.$keycloak.updateToken(60)
|
||||
.then(() => {
|
||||
config.headers.Authorization = 'Bearer ' + this.$keycloak.token
|
||||
resolve(config)
|
||||
})
|
||||
.catch(e => {
|
||||
reject(e)
|
||||
})
|
||||
}
|
||||
}))
|
||||
return instance
|
||||
return this.createAxiosAuth(false)
|
||||
},
|
||||
axiosAuthId () {
|
||||
return this.createAxiosAuth(true)
|
||||
},
|
||||
axiosAuthOptional () {
|
||||
return this.createAxiosAuth(false, true)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createAxiosAuth (includeId = false, optional = false) {
|
||||
let instance = axios.create()
|
||||
instance.interceptors.request.use(config => new Promise((resolve, reject) => {
|
||||
if (!this.$keycloak || !this.$keycloak.authenticated) {
|
||||
reject(new Error('not logged in'))
|
||||
if (optional) {
|
||||
resolve(config)
|
||||
} else {
|
||||
reject(new Error('not logged in'))
|
||||
}
|
||||
} else {
|
||||
this.$keycloak.updateToken(60)
|
||||
.then(() => {
|
||||
config.headers.Authorization = 'Bearer ' + this.$keycloak.token
|
||||
config.headers.id_token = this.$keycloak.idToken
|
||||
if (includeId) {
|
||||
config.headers.id_token = this.$keycloak.idToken
|
||||
}
|
||||
resolve(config)
|
||||
})
|
||||
.catch(e => {
|
||||
|
|
10
src/store.js
10
src/store.js
|
@ -66,7 +66,9 @@ const store = new Vuex.Store({
|
|||
activatorPage: 1,
|
||||
mapType,
|
||||
mapOptions,
|
||||
mapCenter: null
|
||||
mapCenter: null,
|
||||
mapTilerApiKey: null,
|
||||
turnstileToken: null
|
||||
},
|
||||
mutations: {
|
||||
SOCKET_ONOPEN (state, event) {
|
||||
|
@ -157,6 +159,12 @@ const store = new Vuex.Store({
|
|||
},
|
||||
setMapCenter (state, center) {
|
||||
state.mapCenter = center
|
||||
},
|
||||
setMapTilerApiKey (state, key) {
|
||||
state.mapTilerApiKey = key
|
||||
},
|
||||
setTurnstileToken (state, token) {
|
||||
state.turnstileToken = token
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="map-layout" ref="mapLayout">
|
||||
<MglMap v-if="showMap && mapStyle" :apiKey="mapApiKey" :mapStyle="mapStyle" :bounds.sync="bounds" :fitBoundsOptions="fitBoundsOptions" :center="center" :zoom="zoom" :dragRotate="false" :attributionControl="false" class="map" @load="onMapLoaded" @click="onMapClicked" @contextmenu="onMapRightClicked">
|
||||
<MglMap v-if="showMap && mapStyle" :apiKey="mapTilerApiKey" :mapStyle="mapStyle" :bounds.sync="bounds" :fitBoundsOptions="fitBoundsOptions" :center="center" :zoom="zoom" :dragRotate="false" :attributionControl="false" class="map" @load="onMapLoaded" @click="onMapClicked" @contextmenu="onMapRightClicked">
|
||||
<MglGeolocateControl :positionOptions="{ enableHighAccuracy: true }" :fitBoundsOptions="{ maxZoom: 12.5 }" :trackUserLocation="true" position="top-right" />
|
||||
<MglNavigationControl position="top-right" :showCompass="false" />
|
||||
<MglScaleControl position="bottom-left" :unit="mapUnits" />
|
||||
|
@ -33,7 +33,8 @@
|
|||
<div v-if="zoomWarning" class="zoom-warning">Zoom in to see all filtered/spotted summits</div>
|
||||
<SwisstopoInfo />
|
||||
<BasemapAtInfo />
|
||||
<b-loading :is-full-page="false" :active="filtering || !showMap || !mapStyle" />
|
||||
<MapKeyFailedInfo v-if="mapTilerApiKeyFailed" />
|
||||
<b-loading :is-full-page="false" :active="(filtering || !showMap || !mapStyle) && !mapTilerApiKeyFailed" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -57,11 +58,12 @@ import MapDraw from '../components/MapDraw.vue'
|
|||
import MapWebcams from '../components/MapWebcams.vue'
|
||||
import SwisstopoInfo from '../components/SwisstopoInfo.vue'
|
||||
import BasemapAtInfo from '../components/BasemapAtInfo.vue'
|
||||
import MapKeyFailedInfo from '../components/MapKeyFailedInfo.vue'
|
||||
|
||||
export default {
|
||||
name: 'Map',
|
||||
components: {
|
||||
MglMap, MglPopup, MglNavigationControl, MglGeolocateControl, MglScaleControl, MglAttributionControl, MapFilterControl, MapOptionsControl, MapDownloadControl, LoadingRing, SummitPopup, MapRoute, MapInfoPopup, MapDraw, MapWebcams, SwisstopoInfo, BasemapAtInfo
|
||||
MglMap, MglPopup, MglNavigationControl, MglGeolocateControl, MglScaleControl, MglAttributionControl, MapFilterControl, MapOptionsControl, MapDownloadControl, LoadingRing, SummitPopup, MapRoute, MapInfoPopup, MapDraw, MapWebcams, SwisstopoInfo, BasemapAtInfo, MapKeyFailedInfo
|
||||
},
|
||||
mixins: [utils, smptracks, mapstyle, longtouch],
|
||||
created () {
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<div v-if="!enlargeMap" class="column">
|
||||
<div>Coordinates: <Coordinates v-if="summit.coordinates" :latitude="summit.coordinates.latitude" :longitude="summit.coordinates.longitude" :altitude="summit.altitude" :reference="summit.code" /></div>
|
||||
<div>Locator: <span class="locator">{{ locator }}</span></div>
|
||||
<div v-if="$keycloak && $keycloak.authenticated && summit.coordinates">Distance/Bearing: <Bearing :latitude="summit.coordinates.latitude" :longitude="summit.coordinates.longitude" /></div>
|
||||
<div v-if="authenticated && summit.coordinates">Distance/Bearing: <Bearing :latitude="summit.coordinates.latitude" :longitude="summit.coordinates.longitude" /></div>
|
||||
<div v-if="firstActivations">
|
||||
First activation:
|
||||
<FirstActivator :callsign="firstActivations.activators[0].callsign" :userId="firstActivations.activators[0].userId" />
|
||||
|
@ -71,7 +71,7 @@
|
|||
<SummitAttributes :summit-code="summit.code" />
|
||||
|
||||
<template v-if="resources.length > 0">
|
||||
<h6 class="title is-6">Resources<span v-if="$keycloak && $keycloak.authenticated" class="add-article is-size-7-mobile">(<a :href="addArticleLink">+ Article</a> | <a :href="addRouteLink" target="_blank" onclick="return confirm('Routes can be added by uploading tracks on sotamaps.org. They will then automatically appear on SOTLAS as well. Click OK to go to sotamaps.org now.')">+ Route</a>)</span><span v-else class="add-article is-size-7-mobile">(<span class="disabled" title="Log in to add an article">+ Article</span> | <span class="disabled" title="Log in to add a route">+ Route</span>)</span></h6>
|
||||
<h6 class="title is-6">Resources<span v-if="authenticated" class="add-article is-size-7-mobile">(<a :href="addArticleLink">+ Article</a> | <a :href="addRouteLink" target="_blank" onclick="return confirm('Routes can be added by uploading tracks on sotamaps.org. They will then automatically appear on SOTLAS as well. Click OK to go to sotamaps.org now.')">+ Route</a>)</span><span v-else class="add-article is-size-7-mobile">(<span class="disabled" title="Log in to add an article">+ Article</span> | <span class="disabled" title="Log in to add a route">+ Route</span>)</span></h6>
|
||||
<ResourceList :resources="resources" />
|
||||
</template>
|
||||
</div>
|
||||
|
@ -108,7 +108,7 @@
|
|||
<h4 class="title is-4">Photos</h4>
|
||||
<SummitPhotos v-if="summit.photos && summit.photos.length > 0" ref="summitPhotos" :summit="summit" :editable="true" :showWaypointButton="true" @photoDeleted="reloadPhotos" @photoEdited="reloadPhotos" @photosReordered="reloadPhotos" />
|
||||
|
||||
<PhotosUploader v-if="$keycloak && $keycloak.authenticated" :summitCode="summitCode" @upload="reloadPhotos" />
|
||||
<PhotosUploader v-if="authenticated" :summitCode="summitCode" @upload="reloadPhotos" />
|
||||
<div v-else class="uploader-placeholder box"><font-awesome-icon :icon="['far', 'images']" size="lg" /> Log in and upload your photos of this summit!</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -55,13 +55,14 @@ export default defineConfig(async ({ mode }) => {
|
|||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
'vue/dist/vue.esm': 'vue'
|
||||
'@': path.resolve(__dirname, 'src')
|
||||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: [
|
||||
'map-promisified'
|
||||
'map-promisified',
|
||||
'events',
|
||||
'maplibre-gl'
|
||||
]
|
||||
},
|
||||
define: {
|
||||
|
|
Ładowanie…
Reference in New Issue