Skip to content

Commit

Permalink
fixes #28; frontend, backend: await ws connection
Browse files Browse the repository at this point in the history
  • Loading branch information
Limosine committed Sep 15, 2024
1 parent c8c7cec commit d2276e4
Show file tree
Hide file tree
Showing 16 changed files with 252 additions and 123 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
{
"typescript.tsdk": "node_modules\\typescript\\lib",
"deno.enablePaths": ["./backend/", "./common/"],
"deno.enable": true,
"cSpell.words": [
"adminsdk",
"coturn",
"COTURN",
"dayjs",
"fileplay",
"filetransfer",
"Filetransfer",
"filetransfers"
"filetransfers",
"frey",
"GCMAPI",
"hono",
"jose",
"kysely",
"msgpackr",
"nixos",
"postgres",
"webpush"
],
"sqltools.connections": [
{
"previewLimit": 50,
"server": "127.0.0.1",
"port": 5432,
"driver": "PostgreSQL",
"name": "FIleplay (Local)",
"name": "Fileplay (Local)",
"database": "fileplay",
"username": "fileplay",
"password": "fileplay"
Expand Down
26 changes: 0 additions & 26 deletions packages/backend/.vscode/settings.json

This file was deleted.

4 changes: 4 additions & 0 deletions packages/backend/src/ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,16 @@ export const onOpen = async (ws: WSContext, c: Context) => {
(client.device === undefined || client.user === undefined)
) {
console.log("INFO: Authentication failure");

sendMessage(client, { type: "status", data: "unauthorized" });
return client.close(1008, "Unauthorized");
}

// Add to clients
clients.add(client);

sendMessage(client, { type: "status", data: "authorized" });

// Notify devices
if (client.user !== undefined) deviceStateChanged(constants.db, client.user);
};
Expand Down
8 changes: 0 additions & 8 deletions packages/common/.vscode/settings.json

This file was deleted.

24 changes: 18 additions & 6 deletions packages/common/api/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ function type<T extends string>(...t: [T, ...T[]]) {

// Messages from server

const status = z.object({
type: type("status"),
data: type("authorized", "unauthorized"),
});

export const user = z.object({
type: type("user"),
data: z.object({
Expand Down Expand Up @@ -320,6 +325,7 @@ const messageFromServerSchemaWithoutId = z.union([
closeConnection,
codeRedeemed,
error,
status,
]);

export const messageFromServerSchema = messageFromServerSchemaWithoutId.and(
Expand All @@ -328,11 +334,17 @@ export const messageFromServerSchema = messageFromServerSchemaWithoutId.and(
})
);

export type MessageFromServer = z.infer<typeof messageFromServerSchemaWithoutId>;
export type MessageFromServer = z.infer<
typeof messageFromServerSchemaWithoutId
>;

export type ResponseMap<T> =
T extends z.infer<typeof createTransfer> ? Promise<z.infer<typeof filetransfer>["data"]> :
T extends z.infer<typeof createContactCode> ? Promise<z.infer<typeof linkingCode>["data"]> :
T extends z.infer<typeof createDeviceCode> ? Promise<z.infer<typeof linkingCode>["data"]> :
T extends z.infer<typeof getTurnCredentials> ? Promise<z.infer<typeof turnCredentials>["data"]> :
undefined;
T extends z.infer<typeof createTransfer>
? Promise<z.infer<typeof filetransfer>["data"]>
: T extends z.infer<typeof createContactCode>
? Promise<z.infer<typeof linkingCode>["data"]>
: T extends z.infer<typeof createDeviceCode>
? Promise<z.infer<typeof linkingCode>["data"]>
: T extends z.infer<typeof getTurnCredentials>
? Promise<z.infer<typeof turnCredentials>["data"]>
: undefined;
2 changes: 1 addition & 1 deletion packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"pwa-asset-generator": "^6.3.1",
"qrcode": "^1.5.3",
"simple-peer": "^9.11.1",
"svelte": "^5.0.0-next.154",
"svelte": "^5.0.0-next.246",
"svelte-check": "^3.8.0",
"tslib": "^2.6.3",
"typescript": "^5.4.5",
Expand Down
38 changes: 23 additions & 15 deletions packages/frontend/src/lib/api/client.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { browser } from "$app/environment";
import { pack, unpack } from "msgpackr";
import { get, readable, writable } from "svelte/store";
import type { MaybePromise } from "@sveltejs/kit";

import { error } from "$lib/lib/error.svelte";
import { peer } from "$lib/lib/p2p";
import {
closeDialog,
Expand All @@ -12,7 +12,6 @@ import {
dialogProperties,
groupDevices,
groups,
offline,
user,
userParams,
} from "$lib/lib/UI";
Expand Down Expand Up @@ -85,6 +84,7 @@ class HTTPClient {
}

class WebSocketClient {
connected = writable(false);
private socket: WebSocket;
private messageId: number;
private promises: ((value: any) => void)[];
Expand All @@ -98,21 +98,21 @@ class WebSocketClient {
this.socket = this.connect();
}

private onOpen = () => {
if (!onGuestPage()) {
this.sendMessage({ type: "deleteTransfer" });
this.sendMessage({ type: "getInfos" });
}
this.sendBuffered();
};

private connect() {
this.socket = new WebSocket(
`${browser && location.protocol == "https:" ? "wss:" : "ws:"}//${location.host}/api/websocket?type=${onGuestPage() ? "guest" : "main"}`,
);

this.socket.binaryType = "arraybuffer";

this.socket.addEventListener("open", () => {
if (!onGuestPage()) {
this.sendMessage({ type: "deleteTransfer" });
this.sendMessage({ type: "getInfos" });
}
this.sendBuffered();
});

this.socket.addEventListener("message", (event) => {
let data;
if (event.data instanceof ArrayBuffer) {
Expand All @@ -132,18 +132,19 @@ class WebSocketClient {
"WebSocket closed" + (event.reason ? ", reason: " + event.reason : "."),
);

if (get(this.connected)) this.connected.set(false);
get(peer).closeConnections("websocket");

if (event.code !== 1008) {
if (get(offline) === true) {
const unsubscribe = offline.subscribe(async (offline) => {
if (!offline) {
if (get(error.error) !== false) {
const unsubscribe = error.error.subscribe(async (error) => {
if (error === false) {
unsubscribe();
this.connect();
}
});
} else setTimeout(() => this.connect(), 5000);
} else location.href = "/setup";
}
});

return this.socket;
Expand Down Expand Up @@ -182,7 +183,14 @@ class WebSocketClient {
}

private handleData(message: MessageFromServer & { id: number }) {
if (message.type == "user") {
if (message.type == "status") {
if (message.data == "authorized" && !get(this.connected)) {
this.connected.set(true);
this.onOpen();
} else if (message.data == "unauthorized") {
error.unauthorized();
}
} else if (message.type == "user") {
userParams.set({
display_name: message.data.display_name,
avatar_seed: message.data.avatar_seed,
Expand Down
5 changes: 3 additions & 2 deletions packages/frontend/src/lib/dialogs/Privacy.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<script lang="ts">
import { closeDialog, openDialog, registration } from "$lib/lib/UI";
import { settings } from "$lib/lib/settings.svelte";
const click = async () => {
localStorage.setItem("privacyAccepted", "true");
settings.set("privacyAccepted", "true");
await closeDialog(true);
if (
localStorage.getItem("subscribedToPush") === null &&
settings.settings["subscribedToPush"] === undefined &&
$registration !== undefined
)
openDialog({ mode: "request" });
Expand Down
5 changes: 3 additions & 2 deletions packages/frontend/src/lib/dialogs/PushRequest.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import { notifications } from "$lib/lib/notifications";
import { closeDialog, registration } from "$lib/lib/UI";
import { settings } from "$lib/lib/settings.svelte";
</script>

<p style="font-size: large; margin-bottom: 10px;">Enable notifications?</p>
Expand All @@ -11,14 +12,14 @@
<button
class="transparent link"
onclick={() => {
localStorage.setItem("subscribedToPush", "false");
settings.set("subscribedToPush", "false");
closeDialog();
}}>Deny</button
>
<button
class="transparent link"
onclick={() => {
localStorage.setItem("subscribedToPush", "false");
settings.set("subscribedToPush", "false");
closeDialog(true);
$notifications.create($registration);
}}>Allow</button
Expand Down
1 change: 0 additions & 1 deletion packages/frontend/src/lib/lib/UI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export const width = writable(0);
export const layout = derived(width, (width) =>
width < 840 ? "mobile" : "desktop",
);
export const offline = writable(false);

// Navigation
export const path = writable<Routes>({
Expand Down
31 changes: 31 additions & 0 deletions packages/frontend/src/lib/lib/error.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { writable } from "svelte/store";

class VisibleError {
overlay = $state<"" | "hidden">("");
readonly error = writable<false | { icon: string; text: string }>(false);

solved = () => {
this.error.set(false);
this.overlay = "hidden";
};

offline = () => {
this.error.set({
icon: "cloud_off",
text: "Offline, please connect to the internet.",
});
this.overlay = "";
};

unauthorized = () => {
this.error.set({
icon: "warning",
text: "Unauthorized, forwarding to setup.",
});
this.overlay = "";

setTimeout(() => (location.href = "/setup"), 2000);
};
}

export const error = new VisibleError();
3 changes: 2 additions & 1 deletion packages/frontend/src/lib/lib/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { writable } from "svelte/store";

import { PUBLIC_VAPID_KEY } from "$env/static/public";
import { apiClient } from "$lib/api/client";
import { settings } from "$lib/lib/settings.svelte";

class Notifications {
initialized: boolean;
Expand Down Expand Up @@ -31,7 +32,7 @@ class Notifications {
applicationServerKey: PUBLIC_VAPID_KEY,
});

localStorage.setItem("subscribedToPush", JSON.stringify(subscription));
settings.set("subscribedToPush", JSON.stringify(subscription));

apiClient("ws").sendMessage({
type: "updateDevice",
Expand Down
57 changes: 57 additions & 0 deletions packages/frontend/src/lib/lib/settings.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { openDB, type DBSchema, type IDBPDatabase } from "idb";

interface SettingsDB extends DBSchema {
settings: {
value: string;
key: string;
};
}

class Settings {
private database: () => IDBPDatabase<SettingsDB>;
settings = $state<{ [index: string]: string | undefined }>({});
ready = $state(false);

constructor() {
this.database = () => {
throw new Error("Database not initialized");
};
}

init = async () => {
const db = await this.openDatabase();
this.database = () => db;

await this.getAll();
this.ready = true;
};

private openDatabase = () =>
openDB<SettingsDB>("settings-store", 1, {
upgrade(db) {
db.createObjectStore("settings");
},
});

private get = async (key: string) => this.database().get("settings", key);

set = async (key: string, val: string) => {
await this.database().put("settings", val, key);
this.settings[key] = val;
};

clear = async () => this.database().clear("settings");

private keys = async () => this.database().getAllKeys("settings");

private getAll = async () => {
const keys = await this.keys();

for (const key of keys) {
const val = await this.get(key);
if (val !== undefined) this.settings[key] = val;
}
};
}

export const settings = new Settings();
Loading

0 comments on commit d2276e4

Please sign in to comment.