All checks were successful
ci/woodpecker/push/push Pipeline was successful
## Added - CoT (Cursor on Target) server on port 8089 enabling ATAK/iTAK device connectivity - Support for TAK stream protocol and traditional XML CoT messages - TLS/SSL support with automatic fallback to plain TCP - Username/password authentication for CoT connections - Real-time device position tracking with TTL-based expiration (90s default) - API endpoints: `/api/cot/config`, `/api/cot/server-package`, `/api/cot/truststore`, `/api/me/cot-password` - TAK Server section in Settings with QR code for iTAK setup - ATAK password management in Account page for OIDC users - CoT device markers on map showing real-time positions - Comprehensive documentation in `docs/` directory - Environment variables: `COT_PORT`, `COT_TTL_MS`, `COT_REQUIRE_AUTH`, `COT_SSL_CERT`, `COT_SSL_KEY`, `COT_DEBUG` - Dependencies: `fast-xml-parser`, `jszip`, `qrcode` ## Changed - Authentication system supports CoT password management for OIDC users - Database schema includes `cot_password_hash` field - Test suite refactored to follow functional design principles ## Removed - Consolidated utility modules: `authConfig.js`, `authSkipPaths.js`, `bootstrap.js`, `poiConstants.js`, `session.js` ## Security - XML entity expansion protection in CoT parser - Enhanced input validation and SQL injection prevention - Authentication timeout to prevent hanging connections ## Breaking Changes - Port 8089 must be exposed for CoT server. Update firewall rules and Docker/Kubernetes configurations. ## Migration Notes - OIDC users must set ATAK password via Account settings before connecting - Docker: expose port 8089 (`-p 8089:8089`) - Kubernetes: update Helm values to expose port 8089 Co-authored-by: Madison Grubb <madison@elastiflow.com> Reviewed-on: #6
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>
|