fix sonarqube findings
Sonarqube Scanner / Prepare environment (push) Successful in 7s
Sonarqube Scanner / Build and test (push) Successful in 20s
Sonarqube Scanner / SonarQube analysis (push) Successful in 52s
Sonarqube Scanner / Deploy to Server (push) Successful in 5s

This commit is contained in:
Florian Zumpe
2026-09-30 23:34:26 +02:00
parent 8f4664282b
commit 607e5450e1
6 changed files with 40 additions and 19 deletions
+9 -1
View File
@@ -264,7 +264,8 @@
background: #f9fbf6; background: #f9fbf6;
} }
#bootstrap-screen .privacy-summary > p { #bootstrap-screen .privacy-summary > p,
#bootstrap-screen .privacy-summary > output {
margin: 0; margin: 0;
color: var(--bootstrap-muted); color: var(--bootstrap-muted);
font-size: .92rem; font-size: .92rem;
@@ -431,3 +432,10 @@
.route-offline-controls { margin: .75rem 0; } .route-offline-controls { margin: .75rem 0; }
.route-offline-controls > a { display: inline-block; padding: .75rem; } .route-offline-controls > a { display: inline-block; padding: .75rem; }
.route-offline-controls > a[hidden] { display: none; } .route-offline-controls > a[hidden] { display: none; }
.storage-usage output,
#route-offline-status {
display: block;
margin: .4rem 0;
}
.storage-usage output[hidden] { display: none; }
+4 -4
View File
@@ -130,7 +130,7 @@
<p>Warte vor dem Losgehen, bis die Downloads abgeschlossen sind. Aktualisieren braucht Internet. Beim Ausschalten dieses Schalters werden nur die App-Kopien entfernt; separat gespeicherte Strecken, lokale Entwürfe und Streckenfortschritt bleiben erhalten.</p> <p>Warte vor dem Losgehen, bis die Downloads abgeschlossen sind. Aktualisieren braucht Internet. Beim Ausschalten dieses Schalters werden nur die App-Kopien entfernt; separat gespeicherte Strecken, lokale Entwürfe und Streckenfortschritt bleiben erhalten.</p>
<p>Die Speicheranzeige schätzt die Belegung dieser Website. Local Storage für Entwürfe hat zusätzlich eine eigene Grenze. Der Browser kann Offline-Dateien bei knappem Speicher entfernen; beim Löschen der Website-Daten gehen sie ebenfalls verloren. Externe Karten werden nicht automatisch gespeichert.</p> <p>Die Speicheranzeige schätzt die Belegung dieser Website. Local Storage für Entwürfe hat zusätzlich eine eigene Grenze. Der Browser kann Offline-Dateien bei knappem Speicher entfernen; beim Löschen der Website-Daten gehen sie ebenfalls verloren. Externe Karten werden nicht automatisch gespeichert.</p>
</section> </section>
<p id="offline-cache-status" role="status" aria-live="polite"></p> <output id="offline-cache-status" aria-live="polite"></output>
<button id="offline-cache-refresh" type="button" hidden>App-Dateien aktualisieren</button> <button id="offline-cache-refresh" type="button" hidden>App-Dateien aktualisieren</button>
</div> </div>
<section class="storage-usage" data-storage-usage aria-label="Lokaler Speicher"> <section class="storage-usage" data-storage-usage aria-label="Lokaler Speicher">
@@ -140,7 +140,7 @@
<meter data-storage-local-meter min="0" max="5242880" value="0" aria-label="Local Storage im Verhältnis zum Richtwert von 5 MiB"></meter> <meter data-storage-local-meter min="0" max="5242880" value="0" aria-label="Local Storage im Verhältnis zum Richtwert von 5 MiB"></meter>
<p data-storage-local-text></p> <p data-storage-local-text></p>
<p>Website-Speicher: Browser-Schätzung für App-Dateien, Offline-Strecken und Medien. Local Storage: separater Speicher für Entwürfe und Einstellungen. Beide Anzeigen beziehen sich nur auf diese Website, nicht auf die gesamte Festplatte.</p> <p>Website-Speicher: Browser-Schätzung für App-Dateien, Offline-Strecken und Medien. Local Storage: separater Speicher für Entwürfe und Einstellungen. Beide Anzeigen beziehen sich nur auf diese Website, nicht auf die gesamte Festplatte.</p>
<p data-storage-warning role="status" hidden></p> <output data-storage-warning hidden></output>
<button data-storage-refresh type="button">Speicheranzeige aktualisieren</button> <button data-storage-refresh type="button">Speicheranzeige aktualisieren</button>
</section> </section>
<p id="storage-cleanup-status" class="storage-cleanup-status" hidden></p> <p id="storage-cleanup-status" class="storage-cleanup-status" hidden></p>
@@ -233,7 +233,7 @@
<button id="route-offline-save" type="button">Strecke offline speichern</button> <button id="route-offline-save" type="button">Strecke offline speichern</button>
<button id="route-offline-remove" type="button" hidden>Offline-Strecke entfernen</button> <button id="route-offline-remove" type="button" hidden>Offline-Strecke entfernen</button>
<a id="route-gpx-download" download hidden>GPX herunterladen</a> <a id="route-gpx-download" download hidden>GPX herunterladen</a>
<p id="route-offline-status" role="status" aria-live="polite"></p> <output id="route-offline-status" aria-live="polite"></output>
<p>Die Offline-Funktion speichert diese Strecke samt GPX, Bildern und Audio im Browser. Der GPX-Download speichert nur die GPX-Datei im Download-Ordner.</p> <p>Die Offline-Funktion speichert diese Strecke samt GPX, Bildern und Audio im Browser. Der GPX-Download speichert nur die GPX-Datei im Download-Ordner.</p>
</section> </section>
<dl> <dl>
@@ -461,7 +461,7 @@
<meter data-storage-local-meter min="0" max="5242880" value="0" aria-label="Local Storage im Verhältnis zum Richtwert von 5 MiB"></meter> <meter data-storage-local-meter min="0" max="5242880" value="0" aria-label="Local Storage im Verhältnis zum Richtwert von 5 MiB"></meter>
<p data-storage-local-text></p> <p data-storage-local-text></p>
<p>Website-Speicher: Browser-Schätzung für App-Dateien, Offline-Strecken und Medien. Local Storage: separater Speicher für Entwürfe und Einstellungen. Beide Anzeigen beziehen sich nur auf diese Website, nicht auf die gesamte Festplatte.</p> <p>Website-Speicher: Browser-Schätzung für App-Dateien, Offline-Strecken und Medien. Local Storage: separater Speicher für Entwürfe und Einstellungen. Beide Anzeigen beziehen sich nur auf diese Website, nicht auf die gesamte Festplatte.</p>
<p data-storage-warning role="status" hidden></p> <output data-storage-warning hidden></output>
<button data-storage-refresh type="button">Speicheranzeige aktualisieren</button> <button data-storage-refresh type="button">Speicheranzeige aktualisieren</button>
</section> </section>
<p class="editor-muted">Veröffentlichte Serverstrecken und ausschließlich in diesem Browser gespeicherte Entwürfe werden getrennt dargestellt.</p> <p class="editor-muted">Veröffentlichte Serverstrecken und ausschließlich in diesem Browser gespeicherte Entwürfe werden getrennt dargestellt.</p>
+5 -1
View File
@@ -132,6 +132,10 @@ globalThis.Wegwichtel ??= {};
if (message && !routeTask) message.textContent = saved if (message && !routeTask) message.textContent = saved
? `${saved.count} Dateien dieser Strecke sind auf dem Gerät gespeichert.` ? `${saved.count} Dateien dieser Strecke sind auf dem Gerät gespeichert.`
: 'Speichert nur diese Strecke mit GPX, Bildern und Audio für die App.'; : 'Speichert nur diese Strecke mit GPX, Bildern und Audio für die App.';
updateGpxDownload(route);
}
function updateGpxDownload(route) {
const gpx = document.getElementById('route-gpx-download'); const gpx = document.getElementById('route-gpx-download');
if (gpx) { if (gpx) {
gpx.hidden = !route.gpxUrl; gpx.hidden = !route.gpxUrl;
@@ -199,7 +203,7 @@ globalThis.Wegwichtel ??= {};
const run = action => { const run = action => {
const route = selectedRoute; const route = selectedRoute;
if (!route) return; if (!route) return;
void action(route).catch(error => { return action(route).catch(error => {
if (selectedRoute !== route) return; if (selectedRoute !== route) return;
const message = document.getElementById('route-offline-status'); const message = document.getElementById('route-offline-status');
if (message) message.textContent = `${error.message} Ein bisheriger Offline-Stand bleibt erhalten.`; if (message) message.textContent = `${error.message} Ein bisheriger Offline-Stand bleibt erhalten.`;
+14 -6
View File
@@ -33,6 +33,18 @@ globalThis.Wegwichtel ??= {};
} }
} }
function siteDescription(usage, quota, known) {
if (!known) return 'Der Browser meldet keine Speicherkapazität.';
const warning = usage / quota >= .9 ? ' Speicher fast voll.' : '';
return `Website-Speicher: ${format(usage)} von ${format(quota)} belegt (${Math.round(usage / quota * 100)} %). Etwa ${format(Math.max(0, quota - usage))} verfügbar.${warning}`;
}
function localDescription(local) {
if (local == null) return 'Local Storage ist nicht verfügbar.';
const warning = local >= LOCAL_REFERENCE * .9 ? ' Richtwert fast erreicht: Große Bilder können das Speichern verhindern.' : '';
return `Local Storage: ungefähr ${format(local)} belegt. Balken bezogen auf einen Richtwert von 5 MiB; die tatsächliche Grenze hängt vom Browser ab.${warning}`;
}
async function refresh() { async function refresh() {
if (refreshTask) return refreshTask; if (refreshTask) return refreshTask;
refreshTask = (async () => { refreshTask = (async () => {
@@ -41,14 +53,10 @@ globalThis.Wegwichtel ??= {};
const usage = estimate?.usage; const usage = estimate?.usage;
const quota = estimate?.quota; const quota = estimate?.quota;
const known = Number.isFinite(usage) && Number.isFinite(quota) && quota > 0; const known = Number.isFinite(usage) && Number.isFinite(quota) && quota > 0;
show('site', usage, known ? quota : null, known show('site', usage, known ? quota : null, siteDescription(usage, quota, known));
? `Website-Speicher: ${format(usage)} von ${format(quota)} belegt (${Math.round(usage / quota * 100)} %). Etwa ${format(Math.max(0, quota - usage))} verfügbar.${usage / quota >= .9 ? ' Speicher fast voll.' : ''}`
: 'Der Browser meldet keine Speicherkapazität.');
let local = null; let local = null;
try { local = localBytes(); } catch { /* Storage can be blocked by browser settings. */ } try { local = localBytes(); } catch { /* Storage can be blocked by browser settings. */ }
show('local', local, local == null ? null : LOCAL_REFERENCE, local == null show('local', local, local == null ? null : LOCAL_REFERENCE, localDescription(local));
? 'Local Storage ist nicht verfügbar.'
: `Local Storage: ungefähr ${format(local)} belegt. Balken bezogen auf einen Richtwert von 5 MiB; die tatsächliche Grenze hängt vom Browser ab.${local >= LOCAL_REFERENCE * .9 ? ' Richtwert fast erreicht: Große Bilder können das Speichern verhindern.' : ''}`);
for (const panel of document.querySelectorAll('[data-storage-usage]')) { for (const panel of document.querySelectorAll('[data-storage-usage]')) {
const warning = panel.querySelector('[data-storage-warning]'); const warning = panel.querySelector('[data-storage-warning]');
if (warning) { warning.hidden = !lastError; warning.textContent = lastError; } if (warning) { warning.hidden = !lastError; warning.textContent = lastError; }
+7 -6
View File
@@ -2,8 +2,8 @@
// Serve app files and individually selected route snapshots independently (v2). // Serve app files and individually selected route snapshots independently (v2).
importScripts('/js/offline-storage.js'); importScripts('/js/offline-storage.js');
self.addEventListener('install', event => event.waitUntil(self.skipWaiting())); globalThis.addEventListener('install', event => event.waitUntil(globalThis.skipWaiting()));
self.addEventListener('activate', event => event.waitUntil(self.clients.claim())); globalThis.addEventListener('activate', event => event.waitUntil(globalThis.clients.claim()));
/** Audio seeks need partial responses even when the entire file is stored locally. */ /** Audio seeks need partial responses even when the entire file is stored locally. */
async function rangedResponse(response, range) { async function rangedResponse(response, range) {
@@ -11,7 +11,8 @@ async function rangedResponse(response, range) {
const match = /^bytes=(\d*)-(\d*)$/.exec(range); const match = /^bytes=(\d*)-(\d*)$/.exec(range);
if (!match || (!match[1] && !match[2])) return new Response(null, { status: 416, headers: { 'Content-Range': `bytes */${bytes.byteLength}` } }); if (!match || (!match[1] && !match[2])) return new Response(null, { status: 416, headers: { 'Content-Range': `bytes */${bytes.byteLength}` } });
const start = match[1] ? Number(match[1]) : Math.max(0, bytes.byteLength - Number(match[2])); const start = match[1] ? Number(match[1]) : Math.max(0, bytes.byteLength - Number(match[2]));
const end = match[1] ? (match[2] ? Math.min(Number(match[2]), bytes.byteLength - 1) : bytes.byteLength - 1) : bytes.byteLength - 1; let end = bytes.byteLength - 1;
if (match[1] && match[2]) end = Math.min(Number(match[2]), end);
if (start > end || start >= bytes.byteLength) return new Response(null, { status: 416, headers: { 'Content-Range': `bytes */${bytes.byteLength}` } }); if (start > end || start >= bytes.byteLength) return new Response(null, { status: 416, headers: { 'Content-Range': `bytes */${bytes.byteLength}` } });
const headers = new Headers(response.headers); const headers = new Headers(response.headers);
headers.set('Content-Range', `bytes ${start}-${end}/${bytes.byteLength}`); headers.set('Content-Range', `bytes ${start}-${end}/${bytes.byteLength}`);
@@ -20,15 +21,15 @@ async function rangedResponse(response, range) {
return new Response(bytes.slice(start, end + 1), { status: 206, headers }); return new Response(bytes.slice(start, end + 1), { status: 206, headers });
} }
self.addEventListener('fetch', event => { globalThis.addEventListener('fetch', event => {
const request = event.request; const request = event.request;
const url = new URL(request.url); const url = new URL(request.url);
if (request.method !== 'GET' || url.origin !== self.location.origin if (request.method !== 'GET' || url.origin !== globalThis.location.origin
|| request.headers.has('X-Wegwichtel-Offline-Download') || request.headers.has('X-Wegwichtel-Offline-Download')
|| url.pathname === '/api/offline-manifest') return; || url.pathname === '/api/offline-manifest') return;
event.respondWith((async () => { event.respondWith((async () => {
// A complete snapshot is cache-first, avoiding any wait for a failing mobile network. // A complete snapshot is cache-first, avoiding any wait for a failing mobile network.
const cached = await self.Wegwichtel.OfflineStore.match(request.url).catch(() => null); const cached = await globalThis.Wegwichtel.OfflineStore.match(request.url).catch(() => null);
if (cached) { if (cached) {
const range = request.headers.get('Range'); const range = request.headers.get('Range');
return range ? rangedResponse(cached, range) : cached; return range ? rangedResponse(cached, range) : cached;
+1 -1
View File
@@ -16,5 +16,5 @@ export function offlineManifest(publicDirectory) {
} }
} }
visit(publicDirectory); visit(publicDirectory);
return { resources: [...resources].sort() }; return { resources: [...resources].sort((left, right) => left.localeCompare(right, 'en')) };
} }