feat: automate live import timer and add legal source attribution page
This commit is contained in:
parent
9b3fe95683
commit
da7196fa78
5 changed files with 97 additions and 20 deletions
|
|
@ -98,6 +98,15 @@ Admin-Setup (nur beim ersten Start):
|
|||
- Kein Device Fingerprinting
|
||||
- Missbrauchsschutz nur ueber `hashed_device = HMAC_SHA256(device_token, server_salt)`
|
||||
|
||||
## Attribution (Rechtliches)
|
||||
|
||||
Unter `Attribution` muessen die verwendeten Datenquellen und Lizenzen klar genannt werden:
|
||||
- OpenStreetMap-Mitwirkende + ODbL-Hinweis
|
||||
- Geocoding-Dienst (Nominatim) als Quelle
|
||||
- Jede OpenData-Quelle mit Lizenzhinweis (z. B. Datenlizenz Deutschland Zero/By)
|
||||
|
||||
In der App ist dafuer die Seite `quellen.html` vorgesehen.
|
||||
|
||||
## Produktion
|
||||
|
||||
- `STAYSENSE_SERVER_SALT` zwingend als geheimes Env setzen
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ Type=oneshot
|
|||
User=www-data
|
||||
Group=www-data
|
||||
WorkingDirectory=/opt/staysense/backend
|
||||
ExecStart=/usr/bin/python3 /opt/staysense/backend/run_import_jobs.py --config /opt/staysense/docs/open_data_sources.json --prune-legacy
|
||||
ExecStart=/usr/bin/python3 /opt/staysense/backend/run_import_jobs.py --config /opt/staysense/docs/open_data_sources_nrw_live.json --prune-legacy
|
||||
|
|
|
|||
15
src/app.js
15
src/app.js
|
|
@ -103,21 +103,6 @@ function initialize() {
|
|||
btn.addEventListener("click", () => sendSignal(btn.dataset.signal));
|
||||
});
|
||||
|
||||
document.getElementById("show-attribution").addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
legalOutputEl.textContent = "Kartendaten: OpenStreetMap-Mitwirkende (ODbL). Open Data NRW: jeweilige Quellen mit Namensnennung.";
|
||||
});
|
||||
|
||||
document.getElementById("show-privacy").addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
legalOutputEl.textContent = "Kein Login, keine IP-Speicherung, kein Fingerprinting. Missbrauchsschutz via gehashtem lokalem Zufallstoken (HMAC-SHA256).";
|
||||
});
|
||||
|
||||
document.getElementById("show-imprint").addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
legalOutputEl.textContent = "MVP-Hinweis: Impressum im Produktionsbetrieb verpflichtend mit Anbieterkennzeichnung.";
|
||||
});
|
||||
|
||||
adminSetupSubmitEl.addEventListener("click", adminBootstrap);
|
||||
adminLoginSubmitEl.addEventListener("click", adminLogin);
|
||||
adminLogoutEl.addEventListener("click", adminLogout);
|
||||
|
|
|
|||
|
|
@ -182,11 +182,11 @@
|
|||
|
||||
<h3>Rechtliches</h3>
|
||||
<ul class="legal">
|
||||
<li><a href="#" id="show-attribution">Attribution (OSM/ODbL)</a></li>
|
||||
<li><a href="#" id="show-privacy">Datenschutz</a></li>
|
||||
<li><a href="#" id="show-imprint">Impressum</a></li>
|
||||
<li><a href="quellen.html" id="show-attribution">Attribution / Quellen</a></li>
|
||||
<li><a href="https://vanityontour.de/datenschutz/" id="show-privacy" target="_blank" rel="noopener noreferrer">Datenschutz</a></li>
|
||||
<li><a href="https://vanityontour.de/impressum/" id="show-imprint" target="_blank" rel="noopener noreferrer">Impressum</a></li>
|
||||
</ul>
|
||||
<div id="legal-output" class="legal-output"></div>
|
||||
<div id="legal-output" class="legal-output">Attribution: Details und Datenquellen siehe „Attribution / Quellen“.</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
|
|
|
|||
83
src/quellen.html
Normal file
83
src/quellen.html
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>StaySense Quellen & Attribution</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="background haze-a"></div>
|
||||
<div class="background haze-b"></div>
|
||||
|
||||
<header class="top">
|
||||
<h1>Quellen & Attribution</h1>
|
||||
<p>Transparenz zu Datenquellen, Lizenzen und rechtlichen Hinweisen.</p>
|
||||
<small><a href="/" style="color: inherit">Zurück zur App</a></small>
|
||||
</header>
|
||||
|
||||
<main class="grid" style="max-width: 980px">
|
||||
<section class="panel">
|
||||
<h2>Attribution (Pflichtangaben)</h2>
|
||||
<ul class="legal">
|
||||
<li>OpenStreetMap-Mitwirkende (ODbL): <a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">openstreetmap.org/copyright</a></li>
|
||||
<li>Nominatim-Geocoding (OSM Foundation): <a href="https://nominatim.org/release-docs/develop/api/Overview/" target="_blank" rel="noopener noreferrer">API-Dokumentation</a></li>
|
||||
<li>OpenData NRW/Kommunen: jeweilige Datensatz-Lizenz und Namensnennung je Quelle</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Live-Datenquellen (API)</h2>
|
||||
<p class="small" id="source-status">Lade Datenquellen ...</p>
|
||||
<div id="source-list" class="admin-list">-</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Rechtliches</h2>
|
||||
<ul class="legal">
|
||||
<li><a href="https://vanityontour.de/impressum/" target="_blank" rel="noopener noreferrer">Impressum</a></li>
|
||||
<li><a href="https://vanityontour.de/datenschutz/" target="_blank" rel="noopener noreferrer">Datenschutz</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
const DEFAULT_API_BASE =
|
||||
window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1"
|
||||
? "http://127.0.0.1:8787"
|
||||
: "/api";
|
||||
const API_BASE = window.STAYSENSE_API_BASE || DEFAULT_API_BASE;
|
||||
|
||||
async function loadSources() {
|
||||
const statusEl = document.getElementById("source-status");
|
||||
const listEl = document.getElementById("source-list");
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/health`, { cache: "no-store" });
|
||||
if (!response.ok) {
|
||||
throw new Error("health_failed");
|
||||
}
|
||||
const payload = await response.json();
|
||||
const sources = payload.sources || [];
|
||||
if (!sources.length) {
|
||||
statusEl.textContent = "Keine Quellenmetadaten vorhanden.";
|
||||
listEl.textContent = "Keine Einträge.";
|
||||
return;
|
||||
}
|
||||
statusEl.textContent = `Geladen: ${sources.length} Quelle(n).`;
|
||||
listEl.innerHTML = "";
|
||||
sources.forEach((src) => {
|
||||
const div = document.createElement("div");
|
||||
div.className = "admin-list-item";
|
||||
div.textContent = `${src.source_name} | ${src.record_count} records | ${src.imported_at} | ${src.notes}`;
|
||||
listEl.appendChild(div);
|
||||
});
|
||||
} catch {
|
||||
statusEl.textContent = "Quellen konnten nicht geladen werden.";
|
||||
listEl.textContent = "API nicht erreichbar.";
|
||||
}
|
||||
}
|
||||
|
||||
loadSources();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue