diff --git a/.gitignore b/.gitignore index 11ad1db..be88562 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ vite.config.ts.timestamp-* # Paraglide src/lib/paraglide +build +build_gz \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index 4fa0657..8547ae7 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -37,7 +37,8 @@ export default ts.config( }, { rules: { - 'svelte/no-at-html-tags': 'off' + 'svelte/no-at-html-tags': 'off', + '@typescript-eslint/no-unsafe-function-type': 'off' } } ); diff --git a/messages/de-DE.json b/messages/de-DE.json index 32a8ab8..f640e50 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -118,6 +118,12 @@ "viewRelease": "Veröffentlichung anzeigen", "autoUpdate": "Update installieren (experimentell)", "autoUpdateInProgress": "Automatische Aktualisierung läuft, bitte warten..." + }, + "system": { + "firmwareOutdated": "WebUI und Firmware sind nicht synchronisiert. Dies kann zu unerwartetem Verhalten führen." + }, + "home": { + "title": "Start" } }, "colors": { diff --git a/messages/en-US.json b/messages/en-US.json index 41f093d..c005ad7 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -92,7 +92,8 @@ "dndStartHour": "Start hour", "dndStartMinute": "Start minute", "dndEndHour": "End hour", - "dndEndMinute": "End minute" + "dndEndMinute": "End minute", + "localPoolEndpoint": "Local Pool Endpoint" }, "control": { "systemInfo": "System info", @@ -138,6 +139,12 @@ "viewRelease": "View Release", "autoUpdate": "Install update (experimental)", "autoUpdateInProgress": "Auto-update in progress, please wait..." + }, + "system": { + "firmwareOutdated": "WebUI and firmware are out of sync. This might cause unexpected behavior." + }, + "home": { + "title": "Home" } }, "colors": { diff --git a/messages/es-ES.json b/messages/es-ES.json index 2937974..be6e04c 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -118,6 +118,12 @@ "viewRelease": "Ver lanzamiento", "autoUpdate": "Instalar actualización (experimental)", "autoUpdateInProgress": "Actualización automática en progreso, espere..." + }, + "system": { + "firmwareOutdated": "La interfaz web y el firmware no están sincronizados. Esto podría causar un comportamiento inesperado." + }, + "home": { + "title": "Inicio" } }, "button": { diff --git a/messages/nl-NL.json b/messages/nl-NL.json index ebad607..6183472 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -108,6 +108,12 @@ "viewRelease": "Bekijk publicatie", "autoUpdate": "Update installeren (experimenteel)", "autoUpdateInProgress": "Automatische update wordt uitgevoerd. Even geduld a.u.b...." + }, + "system": { + "firmwareOutdated": "WebUI en firmware zijn niet gesynchroniseerd. Dit kan onverwacht gedrag veroorzaken." + }, + "home": { + "title": "Overzicht" } }, "colors": { diff --git a/package.json b/package.json index 29ea8d7..4a861c9 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,8 @@ "@fontsource-variable/oswald": "^5.2.5", "@fontsource/ubuntu": "^5.2.5", "@inlang/paraglide-js": "^2.0.0", - "daisyui": "^5.0.35" + "daisyui": "^5.0.35", + "nostr-tools": "^2.12.0" }, "pnpm": { "onlyBuiltDependencies": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3ed79a1..34cdac2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,9 @@ importers: daisyui: specifier: ^5.0.35 version: 5.0.35 + nostr-tools: + specifier: ^2.12.0 + version: 2.12.0(typescript@5.8.3) devDependencies: '@eslint/compat': specifier: ^1.2.5 @@ -414,6 +417,23 @@ packages: resolution: {integrity: sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==} engines: {node: '>=18'} + '@noble/ciphers@0.5.3': + resolution: {integrity: sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==} + + '@noble/curves@1.1.0': + resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==} + + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + + '@noble/hashes@1.3.1': + resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} + engines: {node: '>= 16'} + + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -534,6 +554,15 @@ packages: cpu: [x64] os: [win32] + '@scure/base@1.1.1': + resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} + + '@scure/bip32@1.3.1': + resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==} + + '@scure/bip39@1.2.1': + resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} + '@sinclair/typebox@0.31.28': resolution: {integrity: sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==} @@ -1569,6 +1598,17 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + nostr-tools@2.12.0: + resolution: {integrity: sha512-pUWEb020gTvt1XZvTa8AKNIHWFapjsv2NKyk43Ez2nnvz6WSXsrTFE0XtkNLSRBjPn6EpxumKeNiVzLz74jNSA==} + peerDependencies: + typescript: '>=5.0.0' + peerDependenciesMeta: + typescript: + optional: true + + nostr-wasm@0.1.0: + resolution: {integrity: sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==} + nwsapi@2.2.20: resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} @@ -2473,6 +2513,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@noble/ciphers@0.5.3': {} + + '@noble/curves@1.1.0': + dependencies: + '@noble/hashes': 1.3.1 + + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + + '@noble/hashes@1.3.1': {} + + '@noble/hashes@1.3.2': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2551,6 +2605,19 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.40.1': optional: true + '@scure/base@1.1.1': {} + + '@scure/bip32@1.3.1': + dependencies: + '@noble/curves': 1.1.0 + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 + + '@scure/bip39@1.2.1': + dependencies: + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 + '@sinclair/typebox@0.31.28': {} '@sqlite.org/sqlite-wasm@3.48.0-build4': {} @@ -3589,6 +3656,21 @@ snapshots: negotiator@1.0.0: {} + nostr-tools@2.12.0(typescript@5.8.3): + dependencies: + '@noble/ciphers': 0.5.3 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 + '@scure/bip32': 1.3.1 + '@scure/bip39': 1.2.1 + optionalDependencies: + nostr-wasm: 0.1.0 + typescript: 5.8.3 + + nostr-wasm@0.1.0: + optional: true + nwsapi@2.2.20: {} object-assign@4.1.1: {} diff --git a/src/app.css b/src/app.css index 64d0745..5f0947d 100644 --- a/src/app.css +++ b/src/app.css @@ -1,3 +1,5 @@ +@source "./safelist.txt"; + @import 'tailwindcss'; @plugin "daisyui" { } diff --git a/src/lib/clockControl.ts b/src/lib/clockControl.ts index 29a965e..c15678f 100644 --- a/src/lib/clockControl.ts +++ b/src/lib/clockControl.ts @@ -109,3 +109,99 @@ export const toggleDoNotDisturb = (currentStatus: boolean) => (e: Event) => { fetch(`${baseUrl}/api/dnd/disable`); } }; + +/** + * Uploads a file to a specified endpoint with progress, success and error callbacks + * @param file The file to upload + * @param endpoint The endpoint to upload to + * @param callbacks Optional callbacks for various events during upload + * @returns A promise that resolves when the upload is complete or fails + */ +export const uploadFile = async ( + file: File | null, + endpoint: string, + callbacks?: { + onProgress?: (progress: number) => void; + onSuccess?: () => void; + onError?: (error?: Error | string | unknown) => void; + } +) => { + if (!file) return; + + const formData = new FormData(); + formData.append('file', file); + + try { + const xhr = new XMLHttpRequest(); + xhr.open('POST', endpoint); + + xhr.upload.onprogress = (event: ProgressEvent) => { + if (event.lengthComputable) { + const progress = Math.round((event.loaded * 100) / event.total); + callbacks?.onProgress?.(progress); + } + }; + + xhr.onload = () => { + if (xhr.status === 200 && xhr.responseText !== 'FAIL') { + callbacks?.onSuccess?.(); + } else { + callbacks?.onError?.(xhr.responseText); + } + }; + + xhr.onerror = () => { + callbacks?.onError?.('Network error'); + }; + + xhr.send(formData); + } catch (error) { + callbacks?.onError?.(error); + console.error(error); + } +}; + +export const uploadFirmwareFile = ( + firmwareUploadFile: File, + callbacks?: { + onProgress?: (progress: number) => void; + onSuccess?: () => void; + onError?: (error?: Error | string | unknown) => void; + } +) => { + uploadFile(firmwareUploadFile, `${baseUrl}/upload/firmware`, callbacks); +}; + +export const uploadWebUiFile = ( + firmwareWebUiFile: File, + callbacks?: { + onProgress?: (progress: number) => void; + onSuccess?: () => void; + onError?: (error?: Error | string | unknown) => void; + } +) => { + uploadFile(firmwareWebUiFile, `${baseUrl}/upload/webui`, callbacks); +}; + +/** + * Automatically updates the firmware + */ +export const autoUpdate = async (callbacks?: { + onSuccess?: (message: string) => void; + onError?: (error?: Error | string | unknown) => void; +}) => { + try { + const response = await fetch(`${baseUrl}/api/firmware/auto_update`); + const data = await response.json(); + const message = data.msg; + + if (!response.ok) { + callbacks?.onError?.(message); + } else { + callbacks?.onSuccess?.(message); + } + } catch (error) { + callbacks?.onError?.(error); + console.error('Error during auto-update:', error); + } +}; diff --git a/src/lib/components/BTClock.svelte b/src/lib/components/BTClock.svelte index 2a25bb8..c1a8951 100644 --- a/src/lib/components/BTClock.svelte +++ b/src/lib/components/BTClock.svelte @@ -1,7 +1,4 @@ + +
{helpText}
+ {/if} +