Privacy improvements:
- Add telemetry consent modal - Conditionally load telemetry script - Add telemetry consent to settings - Update and clarify privacy policy - Bump version number
This commit is contained in:
parent
059eae3784
commit
0011bdb751
113
src/lib/popover/analytics-consent.svelte
Normal file
113
src/lib/popover/analytics-consent.svelte
Normal file
@ -0,0 +1,113 @@
|
||||
<script lang="ts">
|
||||
import { setTelemetryFalse, setTelemetryTrue } from "$lib/stores/SetTelemetryConsent";
|
||||
import { telemetry } from "$lib/stores/telemetryConsent";
|
||||
import { onMount } from "svelte";
|
||||
import { browser } from "$app/environment";
|
||||
|
||||
onMount(() => {
|
||||
if (!localStorage.getItem("telemetryRequested")) {
|
||||
document.querySelector<HTMLDialogElement>("#analytics-consent")?.showModal();
|
||||
}
|
||||
})
|
||||
|
||||
// Setting Function Calls
|
||||
function setAcceptAnalytics() {
|
||||
setTelemetryTrue();
|
||||
localStorage.setItem("telemetryRequested", "yes");
|
||||
document.querySelector<HTMLDialogElement>("#analytics-consent")?.close();
|
||||
}
|
||||
function setDenyAnalytics() {
|
||||
setTelemetryFalse();
|
||||
localStorage.setItem("telemetryRequested", "yes");
|
||||
document.querySelector<HTMLDialogElement>("#analytics-consent")?.close();
|
||||
}
|
||||
|
||||
// Reactively call telemetry script functions
|
||||
$: {
|
||||
if (browser) {
|
||||
if ($telemetry) {
|
||||
loadTelemetryScript();
|
||||
} else {
|
||||
removeTelemetryScript();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadTelemetryScript() {
|
||||
console.log("Activating telemetry script")
|
||||
if (browser) {
|
||||
if (document.querySelector("script[data-entity='owlboard-frontend']")) return; // Prevent duplicate loading
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.type = "module";
|
||||
script.dataset.entity = "owlboard-frontend";
|
||||
script.src = "https://liwan.fjla.uk/script.js";
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
function removeTelemetryScript() {
|
||||
console.log("Deactivating telemetry script")
|
||||
if (browser) {
|
||||
document.querySelector("script[data-entity='owlboard-frontend']")?.remove();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<dialog id="analytics-consent">
|
||||
<h1>Telemetry</h1>
|
||||
<p>
|
||||
OwlBoard collects <strong>anonymous</strong> usage data, such as the most visited pages. Any personal data is anonymized to ensure it cannot be linked to individuals.
|
||||
</p>
|
||||
<p>
|
||||
This data is used to focus efforts, improving the most used features.
|
||||
</p>
|
||||
<p>
|
||||
By selecting Accept, you are helping to steer OwlBoard's future - if
|
||||
you change your mind, head over to Settings.
|
||||
</p>
|
||||
<p>
|
||||
Nobody can be identified using any data stored, all data is available for
|
||||
all to see <a href="https://liwan.fjla.uk" target="_blank">here</a>.
|
||||
</p>
|
||||
<p>
|
||||
Further information can be found in the <a href="/more/privacy">Privacy Policy</a>.
|
||||
</p>
|
||||
<button class="modal-button" type="button" on:click={setAcceptAnalytics}>Accept</button>
|
||||
<button class="modal-button" id="deny-button" type="button" on:click={setDenyAnalytics}>Deny</button>
|
||||
</dialog>
|
||||
|
||||
<style>
|
||||
::backdrop {
|
||||
background-color: var(--main-bg-color);
|
||||
opacity: 75%;
|
||||
}
|
||||
|
||||
#analytics-consent {
|
||||
width: 75vw;
|
||||
max-width: 700px;
|
||||
border-radius: 25px;
|
||||
background-color: var(--island-bg-color);
|
||||
opacity: 100;
|
||||
color: var(--island-text-color);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-button {
|
||||
width: 25%;
|
||||
min-width: 120px;
|
||||
height: 40px;
|
||||
font-size: larger;
|
||||
margin: 5px;
|
||||
border-radius: 30px;
|
||||
background-color: var(--second-bg-color);
|
||||
color: var(--island-text-color);
|
||||
box-shadow: var(--box-shadow);
|
||||
border: none;
|
||||
font-family: urwgothic, sans-serif;
|
||||
}
|
||||
|
||||
#deny-button {
|
||||
background-color: var(--main-bg-color);
|
||||
}
|
||||
</style>
|
14
src/lib/stores/SetTelemetryConsent.ts
Normal file
14
src/lib/stores/SetTelemetryConsent.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { get } from "svelte/store";
|
||||
import { telemetry } from "./telemetryConsent";
|
||||
|
||||
export function setTelemetryTrue() {
|
||||
telemetry.set(true);
|
||||
}
|
||||
|
||||
export function setTelemetryFalse() {
|
||||
telemetry.set(false);
|
||||
}
|
||||
|
||||
export function getTelemetry(): boolean {
|
||||
return get(telemetry);
|
||||
}
|
25
src/lib/stores/telemetryConsent.ts
Normal file
25
src/lib/stores/telemetryConsent.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// src/stores.js
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { browser } from "$app/environment";
|
||||
|
||||
// Initialize the store with a boolean value from local storage
|
||||
export const telemetry: Writable<boolean> = writable(fromLocalStorage("telemetry", false));
|
||||
toLocalStorage(telemetry, "telemetry");
|
||||
|
||||
function fromLocalStorage(storageKey: string, fallback: boolean): boolean {
|
||||
if (browser) {
|
||||
const storedValue = localStorage.getItem(storageKey);
|
||||
if (storedValue !== null && storedValue !== "undefined") {
|
||||
return storedValue === "true";
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function toLocalStorage(store: Writable<boolean>, storageKey: string) {
|
||||
if (browser) {
|
||||
store.subscribe((value: boolean) => {
|
||||
localStorage.setItem(storageKey, String(value));
|
||||
});
|
||||
}
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
export const version: string = "2025.03.4";
|
||||
export const version: string = "2025.03.5";
|
||||
export const versionTag: string = "";
|
||||
|
@ -5,6 +5,7 @@
|
||||
import DevBanner from "$lib/DevBanner.svelte";
|
||||
import { page } from "$app/stores";
|
||||
import { Toaster } from "svelte-french-toast";
|
||||
import AnalyticsConsent from "$lib/popover/analytics-consent.svelte";
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@ -16,8 +17,7 @@
|
||||
Check out the source code on Gitea (https://git.fjla.net/OwlBoard)
|
||||
It's easier to read there
|
||||
-->
|
||||
<!-- Liwan Analytics -->
|
||||
<script type="module" data-entity="owlboard-frontend" src="https://liwan.fjla.uk/script.js"></script>
|
||||
|
||||
<meta name="application-name" content="OwlBoard" />
|
||||
<meta name="author" content="Frederick Boniface" />
|
||||
<meta
|
||||
@ -34,6 +34,7 @@
|
||||
</svelte:head>
|
||||
|
||||
<Toaster />
|
||||
<AnalyticsConsent />
|
||||
|
||||
{#if dev}
|
||||
<DevBanner />
|
||||
|
@ -10,39 +10,57 @@
|
||||
|
||||
<Header {title} />
|
||||
<div>
|
||||
<p>
|
||||
OwlBoard stores the minimum amount of data necessary to provide its functions for your use. No personal data is stored unless you report an issue. To review the specific
|
||||
data that we store, please visit <a href="/more/data">My Data</a>.
|
||||
</p>
|
||||
<p>OwlBoard does not utilize any cookies. IP addresses and browser fingerprints are not logged.</p>
|
||||
<h2>If You Do Not Sign Up</h2>
|
||||
<p>
|
||||
If you choose not to sign up, no personal data will be processed or stored unless you report an issue. Any personal settings are stored locally in your browser and do not
|
||||
leave your device.
|
||||
</p>
|
||||
<h2>If You Sign Up</h2>
|
||||
<p>
|
||||
If you sign up for the rail staff version of OwlBoard, we do require the storage of some data. However, none of this data can be used to personally identify you. Any
|
||||
personal settings are stored locally in your browser and do not leave your device.
|
||||
</p>
|
||||
<p>
|
||||
During the sign-up process, you will be asked to provide a work email address, which will be checked to confirm its origin from a railway company. Once confirmed, an email
|
||||
containing a registration link will be sent to you. At this point, the username portion of your email address is discarded. For example, if your email address is
|
||||
'a-user@owlboard.info', only 'owlboard.info' will be stored. This host portion of your email address is stored to filter and display relevant results prominently.
|
||||
</p>
|
||||
<p>The email server may store the address and message content as part of its regular operation, and your consent to this is implied when you sign up.</p>
|
||||
<p>In addition to the host portion of your email address, a randomly generated UUID is stored for the purpose of authorizing access to the rail staff data.</p>
|
||||
<p>
|
||||
If you enable location data, your location will be sent to the server when you navigate to the homepage to determine your closest stations. This data is never stored on the
|
||||
server after the nearest stations have been send to your device.
|
||||
</p>
|
||||
<h2>Reporting an Issue</h2>
|
||||
<p>When you report an issue, certain data is collected, including your browser's User Agent string and the size of the window in which you are viewing the website.</p>
|
||||
<p>
|
||||
Any data submitted when reporting an issue will be publicly viewable alongside the <a href="https://git.fjla.uk/owlboard/backend/issues" target="_blank"
|
||||
>OwlBoard/backend git repository</a
|
||||
>.
|
||||
</p>
|
||||
<h2>Your Data</h2>
|
||||
<p>
|
||||
OwlBoard logs the IP addresses of its users, this is done on the basis of legitimate
|
||||
interest to ensure the security of the platform and to protect all users from
|
||||
malicious activity. See <a href="#datasharing">Data Sharing</a> for details on how
|
||||
we may share this data.
|
||||
</p>
|
||||
<h3>Telemetry</h3>
|
||||
<p>
|
||||
If you opt-in to Telemetry, you will share your IP address and information about the
|
||||
type of device and software you're using to access the service. This data is
|
||||
anonymised and cannot be traced back to any individual. You can opt-in or opt-out in
|
||||
your <a href="/more/settings">Settings</a>. All of the anonymised data can be viewed
|
||||
at: <a target="_blank" href="https://liwan.fjla.uk">liwan.fjla.uk</a> at any time.
|
||||
</p>
|
||||
<p>
|
||||
Telemetry data is used to identify which areas of the webapp are used most frequently
|
||||
and where improvements need to be made.
|
||||
</p>
|
||||
<h3>Registering</h3>
|
||||
<p>
|
||||
If you register, your email address will be used to verify that you work for an
|
||||
organisation that is permitted to access staff data on the service. An activation
|
||||
email will be sent before your email address is anonymised. For example, if your
|
||||
email address is hello@owlboard.info, it will be anonymized to @owlboard.info.
|
||||
</p>
|
||||
<p>
|
||||
You will be assigned a unique identifier which will be stored alongside your
|
||||
anonymised email address as well as in your browser. This is how the service
|
||||
verifies that you are authorised to access staff data.
|
||||
</p>
|
||||
|
||||
<h2 id="datasharing">Data Sharing</h2>
|
||||
<p>
|
||||
OwlBoard utilises CrowdSec, a security service that helps to protect the platform from
|
||||
malicious activity. As part of its operation, CrowdSec analyzes IP addresses and may
|
||||
share certain information with its community of users to identify and mitigate
|
||||
security threats. If your IP address is identified as part of a security incident or
|
||||
threat, it may be shared with CrowdSec's network for further analysis and to prevent
|
||||
malicious activity. This sharing of IP addresses is done under the legitimate
|
||||
interest basis for ensuring the security of the platform and protecting all users
|
||||
from malicious activity.
|
||||
</p>
|
||||
<p>
|
||||
CrowdSec anonymizes and processes data in accordance with its own privacy policy, which
|
||||
is available for review here. We recommend reviewing their policy to understand how
|
||||
they handle your data.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Nav />
|
||||
@ -54,12 +72,23 @@
|
||||
|
||||
h2 {
|
||||
color: var(--second-text-color);
|
||||
margin: 10px;
|
||||
margin: auto;
|
||||
width: 90vw;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: var(--second-text-color);
|
||||
margin: auto;
|
||||
width: 90vw;
|
||||
padding-top: 10px;
|
||||
}
|
||||
p {
|
||||
color: white;
|
||||
margin: 10px;
|
||||
margin: auto;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 8px;
|
||||
width: 90vw;
|
||||
max-width: 550px;
|
||||
}
|
||||
</style>
|
||||
|
@ -4,6 +4,7 @@
|
||||
import QlSet from "$lib/islands/quick-link-set-island.svelte";
|
||||
import Island from "$lib/islands/island.svelte";
|
||||
import { location } from "$lib/stores/location";
|
||||
import { telemetry } from "$lib/stores/telemetryConsent";
|
||||
import { getCurrentLocation } from "$lib/scripts/getLocation";
|
||||
import toast from "svelte-french-toast";
|
||||
const title = "Settings";
|
||||
@ -12,8 +13,8 @@
|
||||
getCurrentLocation();
|
||||
}
|
||||
|
||||
function locationToast() {
|
||||
toast("Settings updated");
|
||||
function confirmationToast() {
|
||||
toast.success("Settings updated");
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -29,10 +30,19 @@
|
||||
<p>Use your location to quickly check departure boards near you</p>
|
||||
<div class="checkbox-container">
|
||||
<label for="location_enable">Enabled</label>
|
||||
<input id="location_enable" type="checkbox" bind:checked={$location} on:click={locationToast} />
|
||||
<input id="location_enable" type="checkbox" bind:checked={$location} on:click={confirmationToast} />
|
||||
</div>
|
||||
</Island>
|
||||
|
||||
<Island variables={{title:"Telemetry"}}>
|
||||
<p>Telemetry helps shape the future of OwlBoard - all data is anonymised. To find out more, see the
|
||||
<a href="/more/privacy">privacy policy</a>.
|
||||
</p>
|
||||
<div class="checkbox-container">
|
||||
<label for="telemetry_enable">Enabled</label>
|
||||
<input id="telemetry_enable" type="checkbox" bind:checked={$telemetry} on:click={confirmationToast} />
|
||||
</Island>
|
||||
|
||||
<Nav />
|
||||
|
||||
<style>
|
||||
@ -51,4 +61,5 @@
|
||||
margin-right: 25px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user