198 lines
5.6 KiB
Vue
198 lines
5.6 KiB
Vue
<template>
|
||
<div class="p-6">
|
||
<h2 class="kestrel-page-heading mb-4">
|
||
Settings
|
||
</h2>
|
||
|
||
<section class="mb-8">
|
||
<h3 class="kestrel-section-label">
|
||
Map & offline
|
||
</h3>
|
||
<div class="kestrel-card p-4">
|
||
<p class="mb-3 text-sm text-kestrel-text">
|
||
Clear saved map tiles to free storage. The map will load tiles from the network again when you use it.
|
||
</p>
|
||
<p
|
||
v-if="tilesStored !== null"
|
||
class="mb-2 text-xs text-kestrel-muted"
|
||
>
|
||
{{ tilesStored > 0 ? `${tilesStored} tiles stored.` : 'No tiles stored.' }}
|
||
</p>
|
||
<p
|
||
v-if="tilesMessage"
|
||
class="mb-2 text-sm"
|
||
:class="tilesMessageSuccess ? 'text-green-400' : 'text-red-400'"
|
||
>
|
||
{{ tilesMessage }}
|
||
</p>
|
||
<button
|
||
type="button"
|
||
class="kestrel-btn-secondary disabled:opacity-50"
|
||
:disabled="tilesLoading"
|
||
@click="onClearTiles"
|
||
>
|
||
{{ tilesLoading ? 'Clearing…' : 'Clear saved map tiles' }}
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="mb-8">
|
||
<h3 class="kestrel-section-label">
|
||
TAK Server (ATAK / iTAK)
|
||
</h3>
|
||
<div class="kestrel-card p-4">
|
||
<p class="mb-3 text-sm text-kestrel-text">
|
||
Scan this QR code with iTAK (or ATAK) to add this KestrelOS server. You’ll be prompted for your KestrelOS username and password after scanning.
|
||
</p>
|
||
<div
|
||
v-if="takQrDataUrl"
|
||
class="inline-block rounded-lg border border-kestrel-border bg-white p-3"
|
||
>
|
||
<img
|
||
:src="takQrDataUrl"
|
||
alt="TAK Server QR code"
|
||
class="h-48 w-48"
|
||
width="192"
|
||
height="192"
|
||
>
|
||
</div>
|
||
<p
|
||
v-else-if="takQrError"
|
||
class="text-sm text-red-400"
|
||
>
|
||
{{ takQrError }}
|
||
</p>
|
||
<p
|
||
v-else
|
||
class="text-sm text-kestrel-muted"
|
||
>
|
||
Loading QR code…
|
||
</p>
|
||
<p
|
||
v-if="takServerString"
|
||
class="mt-3 text-xs text-kestrel-muted break-all"
|
||
>
|
||
{{ takServerString }}
|
||
</p>
|
||
<template v-if="cotConfig?.ssl">
|
||
<p class="mt-3 text-sm text-kestrel-text">
|
||
This server uses a self-signed certificate. iTAK will not connect until it trusts the cert.
|
||
</p>
|
||
<ol class="mt-2 list-decimal list-inside space-y-1 text-sm text-kestrel-text">
|
||
<li>
|
||
<strong>Upload server package:</strong> Download below, then in iTAK tap Add Server (+) → Upload server package and select the zip; enter KestrelOS username and password when prompted.
|
||
</li>
|
||
<li>
|
||
<strong>Plain TCP:</strong> Remove or rename <code class="bg-kestrel-surface px-1 rounded">.dev-certs</code>, restart, then in iTAK add the server with SSL disabled.
|
||
</li>
|
||
</ol>
|
||
<a
|
||
href="/api/cot/server-package"
|
||
download="kestrelos-itak-server-package.zip"
|
||
class="kestrel-btn-secondary mt-3 inline-block"
|
||
>
|
||
Download server package (zip)
|
||
</a>
|
||
</template>
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<h3 class="kestrel-section-label">
|
||
About
|
||
</h3>
|
||
<div class="kestrel-card p-4">
|
||
<p class="font-medium text-kestrel-text">
|
||
KestrelOS
|
||
</p>
|
||
<p
|
||
v-if="version"
|
||
class="mt-1 text-sm text-kestrel-muted"
|
||
>
|
||
Version {{ version }}
|
||
</p>
|
||
<p class="mt-2 text-xs text-kestrel-muted">
|
||
Tactical Operations Center for OSINT feeds.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
const config = useRuntimeConfig()
|
||
const version = config.public?.version ?? null
|
||
|
||
const tilesStored = ref(null)
|
||
const tilesMessage = ref('')
|
||
const tilesMessageSuccess = ref(false)
|
||
const tilesLoading = ref(false)
|
||
|
||
const cotConfig = ref(null)
|
||
const takQrDataUrl = ref('')
|
||
const takQrError = ref('')
|
||
const takServerString = ref('')
|
||
|
||
async function loadTilesStored() {
|
||
if (typeof window === 'undefined') return
|
||
try {
|
||
const offline = await import('leaflet.offline')
|
||
if (offline.getStorageLength) {
|
||
const n = await offline.getStorageLength()
|
||
tilesStored.value = n
|
||
}
|
||
}
|
||
catch {
|
||
tilesStored.value = null
|
||
}
|
||
}
|
||
|
||
async function onClearTiles() {
|
||
tilesMessage.value = ''
|
||
tilesLoading.value = true
|
||
try {
|
||
const offline = await import('leaflet.offline')
|
||
if (offline.truncate) {
|
||
await offline.truncate()
|
||
tilesStored.value = 0
|
||
tilesMessage.value = 'Saved map tiles cleared.'
|
||
tilesMessageSuccess.value = true
|
||
}
|
||
else {
|
||
tilesMessage.value = 'Could not clear tiles.'
|
||
tilesMessageSuccess.value = false
|
||
}
|
||
}
|
||
catch (e) {
|
||
tilesMessage.value = e?.message ?? 'Failed to clear tiles.'
|
||
tilesMessageSuccess.value = false
|
||
}
|
||
finally {
|
||
tilesLoading.value = false
|
||
}
|
||
}
|
||
|
||
async function loadTakQr() {
|
||
if (typeof window === 'undefined') return
|
||
try {
|
||
const res = await $fetch('/api/cot/config')
|
||
cotConfig.value = res
|
||
const hostname = window.location.hostname
|
||
const port = res?.port ?? 8089
|
||
const protocol = res?.ssl ? 'ssl' : 'tcp'
|
||
const str = `KestrelOS,${hostname},${port},${protocol}`
|
||
takServerString.value = str
|
||
const QRCode = (await import('qrcode')).default
|
||
takQrDataUrl.value = await QRCode.toDataURL(str, { width: 192, margin: 1 })
|
||
}
|
||
catch (e) {
|
||
takQrError.value = e?.data?.error ?? e?.message ?? 'Could not load TAK server config.'
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadTilesStored()
|
||
loadTakQr()
|
||
})
|
||
</script>
|