diff --git a/frontend/bun.lockb b/frontend/bun.lockb index 63e774e..3e7fff7 100755 Binary files a/frontend/bun.lockb and b/frontend/bun.lockb differ diff --git a/frontend/package.json b/frontend/package.json index 1060b4c..05119e0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -23,6 +23,7 @@ "@tauri-apps/plugin-opener": "^2", "clsx": "^2.1.1", "fuse.js": "^7.1.0", + "jwt-decode": "^4.0.0", "solid-js": "^1.9.3", "solid-motionone": "^1.0.3", "tailwind-scrollbar-hide": "^2.0.0", diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index 63395df..7a0decf 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -7,7 +7,6 @@ use std::sync::Arc; use std::sync::Mutex; use tauri::AppHandle; use tauri::Emitter; -use tauri::TitleBarStyle; use tauri::{WebviewUrl, WebviewWindowBuilder}; struct WatcherState { @@ -116,7 +115,7 @@ pub fn run() { .setup(|app| { let win_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default()) .inner_size(480.0, 360.0) - .hidden_title(true) + // .hidden_title(true) .resizable(true); // set transparent title bar only when building for macOS #[cfg(target_os = "macos")] diff --git a/frontend/src/Login.tsx b/frontend/src/Login.tsx index 377276e..344d8cd 100644 --- a/frontend/src/Login.tsx +++ b/frontend/src/Login.tsx @@ -2,6 +2,8 @@ import { Button } from "@kobalte/core/button"; import { TextField } from "@kobalte/core/text-field"; import { createSignal, Show, type Component } from "solid-js"; import { postCode, postLogin } from "./network"; +import { isTokenValid } from "./ProtectedRoute"; +import { Navigate } from "@solidjs/router"; export const Login: Component = () => { let form: HTMLFormElement | undefined; @@ -35,19 +37,23 @@ export const Login: Component = () => { } }; + const isAuthorized = isTokenValid(); + return ( -
- - Email - - - - - Code + }> + + + Email - - - + + + Code + + + + + +
); }; diff --git a/frontend/src/ProtectedRoute.tsx b/frontend/src/ProtectedRoute.tsx new file mode 100644 index 0000000..e4bc3c9 --- /dev/null +++ b/frontend/src/ProtectedRoute.tsx @@ -0,0 +1,30 @@ +import { type Component, type JSX, Show } from "solid-js"; +import { jwtDecode } from "jwt-decode"; +import { Navigate } from "@solidjs/router"; + +export const isTokenValid = (): boolean => { + const token = localStorage.getItem("access"); + + if (token == null) { + return false; + } + + try { + jwtDecode(token); + return true; + } catch (err) { + return false; + } +}; + +export const ProtectedRoute: Component<{ children?: JSX.Element }> = ( + props, +) => { + const isValid = isTokenValid(); + + return ( + }> + {props.children} + + ); +}; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 61dd91b..fb4943b 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -5,13 +5,17 @@ import "./index.css"; import { Route, Router } from "@solidjs/router"; import { ImagePage } from "./ImagePage"; import { Login } from "./Login"; +import { ProtectedRoute } from "./ProtectedRoute"; render( () => ( - - + + + + + ), document.getElementById("root") as HTMLElement, diff --git a/frontend/src/network/index.ts b/frontend/src/network/index.ts index 7e4084b..9c5100c 100644 --- a/frontend/src/network/index.ts +++ b/frontend/src/network/index.ts @@ -19,12 +19,24 @@ type BaseRequestParams = Partial<{ const getBaseRequest = ({ path, body, method }: BaseRequestParams): Request => { return new Request(`http://localhost:3040/${path}`, { - headers: { userId: "1db09f34-b155-4bf2-b606-dda25365fc89" }, body, method, }); }; +const getBaseAuthorizedRequest = ({ + path, + body, + method, +}: BaseRequestParams): Request => { + return new Request(`http://localhost:3040/${path}`, { + headers: { + Authorization: `Bearer ${localStorage.getItem("access")?.toString()}`, + }, + body, + method, + }); +}; const sendImageResponseValidator = strictObject({ ID: pipe(string(), uuid()), ImageID: pipe(string(), uuid()), @@ -35,7 +47,7 @@ export const sendImage = async ( imageName: string, base64Image: string, ): Promise> => { - const request = getBaseRequest({ + const request = getBaseAuthorizedRequest({ path: `image/${imageName}`, body: base64Image, method: "POST", @@ -107,7 +119,7 @@ const getUserImagesResponseValidator = array(dataTypeValidator); export type UserImage = InferOutput; export const getUserImages = async (): Promise => { - const request = getBaseRequest({ path: "image" }); + const request = getBaseAuthorizedRequest({ path: "image" }); const res = await fetch(request).then((res) => res.json());