From 3fcd908d68aa9e66b6cc7853a98fc4799dd26174 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sun, 26 Apr 2026 22:30:46 +0200 Subject: [PATCH] Add config UI for webdav uploader --- daemon/src/config.rs | 6 +- daemon/src/main.rs | 4 +- .../web/src/lib/components/ConfigForm.svelte | 170 ++++++++++++++++++ daemon/web/src/lib/utils.svelte.ts | 13 ++ 4 files changed, 188 insertions(+), 5 deletions(-) diff --git a/daemon/src/config.rs b/daemon/src/config.rs index 1b9c574..b546c73 100644 --- a/daemon/src/config.rs +++ b/daemon/src/config.rs @@ -46,8 +46,8 @@ pub struct Config { pub wifi_enabled: bool, /// Vector containing wifi client DNS servers pub dns_servers: Option>, - /// Optional WebDAV upload configuration. When unset, no upload worker runs. - pub webdav: Option, + /// WebDAV upload configuration. The upload worker runs whenever `webdav.url` is non-empty. + pub webdav: WebdavConfig, } /// Configuration for uploading finished QMDL recordings to a WebDAV server. @@ -105,7 +105,7 @@ impl Default for Config { wifi_security: None, wifi_enabled: false, dns_servers: None, - webdav: None, + webdav: WebdavConfig::default(), } } } diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 1bd55a9..7403e2c 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -288,12 +288,12 @@ async fn run_with_config( wifi_status.clone(), ); - if let Some(webdav_config) = config.webdav.clone() { + if !config.webdav.url.trim().is_empty() { run_webdav_upload_worker( &task_tracker, shutdown_token.clone(), qmdl_store_lock.clone(), - webdav_config.into(), + config.webdav.clone().into(), ); } diff --git a/daemon/web/src/lib/components/ConfigForm.svelte b/daemon/web/src/lib/components/ConfigForm.svelte index 51c2853..15ee184 100644 --- a/daemon/web/src/lib/components/ConfigForm.svelte +++ b/daemon/web/src/lib/components/ConfigForm.svelte @@ -26,12 +26,15 @@ let scanning = $state(false); let scanResults = $state([]); let dnsServersInput = $state(''); + let webdavExpanded = $state(false); + let webdavUrlInput = $state(null); async function load_config() { try { loading = true; config = await get_config(); dnsServersInput = config.dns_servers ? config.dns_servers.join(', ') : ''; + webdavExpanded = config.webdav.url.trim() !== ''; message = ''; messageType = null; poll_wifi_status(); @@ -345,6 +348,173 @@ +
+

WebDAV Upload

+

+ Once a recording has been closed for at least the configured age, both the + .qmdl and .ndjson files are uploaded in the background to the WebDAV server. +

+ +
+ { + webdavExpanded = e.currentTarget.checked; + if (webdavExpanded) { + setTimeout(() => webdavUrlInput?.focus(), 0); + } else { + if (config) config.webdav.url = ''; + } + }} + class="h-4 w-4 text-rayhunter-blue focus:ring-rayhunter-blue border-gray-300 rounded-sm" + /> + +
+ + {#if webdavExpanded} +
+ + { + if (config && config.webdav.url.trim() === '') { + webdavExpanded = false; + } + }} + placeholder="https://dav.example.com/rayhunter/" + class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-hidden focus:ring-2 focus:ring-rayhunter-blue" + /> +

+ Files are uploaded via HTTP PUT under this base URL. No folders are + created, and folders in this base URL are assumed to exist already. +

+
+ +
+ + +

+ Optional. Leave blank for unauthenticated uploads. +

+
+ +
+ + +

+ A password without a username will be rejected and the request will + be sent unauthenticated. +

+
+ +
+ + +
+ +
+ + +

+ How often the worker checks for new entries to upload. +

+
+ +
+ + +

+ How long a recording must be closed before it becomes eligible for + upload. +

+
+ +
+ + +
+

+ When enabled, the local files are removed after a successful upload. + Otherwise the manifest is just marked as uploaded. +

+ {/if} +
+ {#if config.device === 'orbic' || config.device === 'moxee' || config.device === 'tmobile' || config.device === 'wingtech'}

WiFi Client Mode

diff --git a/daemon/web/src/lib/utils.svelte.ts b/daemon/web/src/lib/utils.svelte.ts index 0ddc264..b982093 100644 --- a/daemon/web/src/lib/utils.svelte.ts +++ b/daemon/web/src/lib/utils.svelte.ts @@ -18,6 +18,16 @@ export enum enabled_notifications { LowBattery = 'LowBattery', } +export interface WebdavConfig { + url: string; + username: string | null; + password: string | null; + upload_timeout_secs: number; + poll_interval_secs: number; + min_age_secs: number; + delete_on_upload: boolean; +} + export interface Config { device: string; ui_level: number; @@ -33,6 +43,9 @@ export interface Config { wifi_security: 'wpa_psk' | 'sae' | null; wifi_enabled: boolean; dns_servers: string[] | null; + firewall_restrict_outbound: boolean; + firewall_allowed_ports: number[] | null; + webdav: WebdavConfig; } export interface WifiStatus {