tangled
alpha
login
or
join now
azom.dev
/
PrivacyPin
2
fork
atom
Self-hosted, federated location sharing app and server that prioritizes user privacy and security
end-to-end-encryption
location-sharing
privacy
self-hosted
federated
2
fork
atom
overview
issues
1
pulls
pipelines
Added a toggleable button for location sharing.
kishka.cc
1 month ago
28ec96ab
63a9cb6b
+57
-9
4 changed files
expand all
collapse all
unified
split
app
src
settings-page
settings.html
settings.ts
utils
api.ts
store.ts
+8
app/src/settings-page/settings.html
···
11
11
<div class="actions" x-data="settingsPageState">
12
12
<h3>Settings</h3>
13
13
14
14
+
<div>
15
15
+
Location Sharing
16
16
+
<button
17
17
+
x-text="location_on"
18
18
+
@click="toggleLocation()"
19
19
+
></button>
20
20
+
</div>
21
21
+
14
22
<button class="btn-primary" @click="goto('home')">
15
23
Back to Home
16
24
</button>
+10
app/src/settings-page/settings.ts
···
3
3
import { goto } from "../utils/tools.ts";
4
4
5
5
Alpine.data("settingsPageState", () => ({
6
6
+
location_on: Store.get("is_sharing"),
7
7
+
6
8
async debugLogout() {
7
9
await Store.reset();
8
10
goto("signup");
9
11
},
12
12
+
10
13
goto(newLocation: string) {
11
14
goto(newLocation);
15
15
+
},
16
16
+
17
17
+
async toggleLocation() {
18
18
+
// once i finish this here, i'll move it over to api maybe? i'll have to check with azom
19
19
+
20
20
+
await Store.set("is_sharing", !(await Store.get("is_sharing")));
21
21
+
this.location_on = Store.get("is_sharing");
12
22
},
13
23
}));
14
24
+34
-8
app/src/utils/api.ts
···
7
7
/**
8
8
* This function can throw an error
9
9
*/
10
10
-
export async function createAccount(server_url: string, signup_key: string): Promise<{ user_id: string; is_admin: boolean }> {
11
11
-
const keyPair = await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
10
10
+
export async function createAccount(
11
11
+
server_url: string,
12
12
+
signup_key: string,
13
13
+
): Promise<{ user_id: string; is_admin: boolean }> {
14
14
+
const keyPair = await crypto.subtle.generateKey("Ed25519", true, [
15
15
+
"sign",
16
16
+
"verify",
17
17
+
]);
12
18
const pubKeyRaw = await crypto.subtle.exportKey("raw", keyPair.publicKey);
13
13
-
const privKeyRaw = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
19
19
+
const privKeyRaw = await crypto.subtle.exportKey(
20
20
+
"pkcs8",
21
21
+
keyPair.privateKey,
22
22
+
);
14
23
const pub_key_b64 = bufToBase64(pubKeyRaw);
15
24
16
25
const response = await fetch(server_url + "/create-account", {
···
19
28
body: JSON.stringify({ signup_key, pub_key_b64 }),
20
29
});
21
30
22
22
-
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
31
31
+
if (!response.ok)
32
32
+
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
23
33
const json = await response.json();
24
34
25
35
// TODO validate data?
···
28
38
await Store.set("user_id", json.user_id);
29
39
await Store.set("is_admin", json.is_admin);
30
40
await Store.set("priv_key", bufToBase64(privKeyRaw));
41
41
+
42
42
+
await Store.set("is_sharing", true); // i'm adding this so it works in settings, i think it's more user friendly to have the location to be on by default, but... less privacy friendly? wait no.. nvm. it won't share location with the server since they don't have any added friends yet :)
31
43
32
44
return json;
33
45
}
···
41
53
await post("request-friend-request", friend_id);
42
54
}
43
55
44
44
-
export async function isFriendRequestAccepted(friend_id: string): Promise<boolean> {
56
56
+
export async function isFriendRequestAccepted(
57
57
+
friend_id: string,
58
58
+
): Promise<boolean> {
45
59
const res = await post("is-friend-request-accepted", friend_id);
46
60
return res === "true";
47
61
}
48
62
49
49
-
export async function sendPings(friend_id: string, ping: string): Promise<void> {
63
63
+
export async function sendPings(
64
64
+
friend_id: string,
65
65
+
ping: string,
66
66
+
): Promise<void> {
50
67
// later, accept a list of friend ids, but anyways this specific api won't stay in typescript for long since it needs to be run in the background
51
51
-
await post("send-pings", JSON.stringify([{ receiver_id: friend_id, encrypted_ping: ping }]));
68
68
+
await post(
69
69
+
"send-pings",
70
70
+
JSON.stringify([{ receiver_id: friend_id, encrypted_ping: ping }]),
71
71
+
);
52
72
}
53
73
54
74
export async function getPings(friend_id: string): Promise<string[]> {
···
68
88
69
89
const privKeyBytes = Uint8Array.fromBase64(privKey_b64);
70
90
71
71
-
const privKey = await crypto.subtle.importKey("pkcs8", privKeyBytes.buffer, "Ed25519", false, ["sign"]);
91
91
+
const privKey = await crypto.subtle.importKey(
92
92
+
"pkcs8",
93
93
+
privKeyBytes.buffer,
94
94
+
"Ed25519",
95
95
+
false,
96
96
+
["sign"],
97
97
+
);
72
98
73
99
const signature = await crypto.subtle.sign("Ed25519", privKey, bodyBytes);
74
100
const signature_b64 = bufToBase64(signature);
+5
-1
app/src/utils/store.ts
···
6
6
friends: { name: string; id: string }[];
7
7
is_admin: boolean;
8
8
priv_key: string;
9
9
+
is_sharing: boolean;
9
10
};
10
11
11
12
export const Store = {
···
19
20
return value;
20
21
},
21
22
22
22
-
async set<T extends keyof Settings>(key: T, value: Settings[T]): Promise<void> {
23
23
+
async set<T extends keyof Settings>(
24
24
+
key: T,
25
25
+
value: Settings[T],
26
26
+
): Promise<void> {
23
27
const store = await TauriStore.load("settings.json");
24
28
await store.set(key, value);
25
29
await store.save();