feat(authorization): e2e working authorization

This commit is contained in:
2025-04-11 19:58:25 +01:00
parent 6290c4b843
commit f89de6db50
7 changed files with 70 additions and 18 deletions

Binary file not shown.

View File

@ -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",

View File

@ -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")]

View File

@ -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 (
<form ref={form} onSubmit={onSubmit}>
<TextField name="email">
<TextField.Label>Email</TextField.Label>
<TextField.Input />
</TextField>
<Show when={submitted()}>
<TextField name="code">
<TextField.Label>Code</TextField.Label>
<Show when={!isAuthorized} fallback={<Navigate href="/" />}>
<form ref={form} onSubmit={onSubmit}>
<TextField name="email">
<TextField.Label>Email</TextField.Label>
<TextField.Input />
</TextField>
</Show>
<Button type="submit">Submit</Button>
</form>
<Show when={submitted()}>
<TextField name="code">
<TextField.Label>Code</TextField.Label>
<TextField.Input />
</TextField>
</Show>
<Button type="submit">Submit</Button>
</form>
</Show>
);
};

View File

@ -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 (
<Show when={isValid} fallback={<Navigate href="/login" />}>
{props.children}
</Show>
);
};

View File

@ -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(
() => (
<Router>
<Route path="/" component={App} />
<Route path="/login" component={Login} />
<Route path="/image/:imageId" component={ImagePage} />
<Route path="/" component={ProtectedRoute}>
<Route path="/" component={App} />
<Route path="/image/:imageId" component={ImagePage} />
</Route>
</Router>
),
document.getElementById("root") as HTMLElement,

View File

@ -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<InferOutput<typeof sendImageResponseValidator>> => {
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<typeof dataTypeValidator>;
export const getUserImages = async (): Promise<UserImage[]> => {
const request = getBaseRequest({ path: "image" });
const request = getBaseAuthorizedRequest({ path: "image" });
const res = await fetch(request).then((res) => res.json());