Use Azure auth for no good reason
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { Home } from "@/routes/Home";
|
||||
import { Login } from "@/routes/Login";
|
||||
import { Piece } from "@/routes/Piece";
|
||||
import { Pieces } from "@/routes/Pieces";
|
||||
import { Repertoire } from "@/routes/Repertoire";
|
||||
@@ -63,10 +62,6 @@ const router = createBrowserRouter([
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
Component: Login,
|
||||
},
|
||||
], {
|
||||
future: {
|
||||
v7_fetcherPersist: true,
|
||||
|
||||
@@ -5,8 +5,7 @@ import { client, mapResponse } from "./client";
|
||||
|
||||
export interface User {
|
||||
readonly userId: UserId;
|
||||
readonly username: string;
|
||||
readonly admin: boolean;
|
||||
readonly displayName: string;
|
||||
}
|
||||
|
||||
export interface SystemInformation {
|
||||
@@ -108,7 +107,7 @@ export const denormalizeRepertoire = ({
|
||||
);
|
||||
|
||||
const UserSemaphore = Effect.unsafeMakeSemaphore(1);
|
||||
const CacheSemaphore = Effect.unsafeMakeSemaphore(4);
|
||||
const PieceSemaphore = Effect.unsafeMakeSemaphore(4);
|
||||
const RepertoireSemaphore = Effect.unsafeMakeSemaphore(1);
|
||||
|
||||
export const userLookup = (userId: UserId) => pipe(
|
||||
@@ -123,7 +122,7 @@ export const pieceLookup = (pieceId: PieceId) => pipe(
|
||||
Effect.flatMap(mapResponse),
|
||||
Effect.flatMap(denormalizePiece),
|
||||
Effect.map((x): Piece => x), // safely coerce to interface
|
||||
CacheSemaphore.withPermits(1),
|
||||
PieceSemaphore.withPermits(1),
|
||||
);
|
||||
|
||||
export const repertoireLookup = (repertoireId: RepertoireId) => pipe(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { API_URL_PREFIX } from "@/client";
|
||||
import { mapProp, Update, Updater, useStore } from "@/hooks/useStore";
|
||||
import { Treaty } from "@elysiajs/eden";
|
||||
import { Effect, Fiber, pipe } from "effect";
|
||||
@@ -57,8 +58,7 @@ export function useLoading<R extends Record<number, unknown>>(fn: () => Promise<
|
||||
|
||||
if (error !== null) {
|
||||
if (error.status === 401) {
|
||||
setUser(null);
|
||||
navigate("/login");
|
||||
window.location.href = `${API_URL_PREFIX}/api/v1/login`;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,31 +13,17 @@ export namespace Store {
|
||||
export interface User {
|
||||
readonly userId: UserId;
|
||||
readonly username: string;
|
||||
readonly admin: boolean;
|
||||
readonly roles: readonly string[];
|
||||
}
|
||||
}
|
||||
|
||||
export interface Store {
|
||||
readonly loginUsername: string;
|
||||
readonly loginPassword: string;
|
||||
|
||||
readonly user: Store.User | null;
|
||||
|
||||
readonly setLoginUsername: Updater<string>;
|
||||
readonly setLoginPassword: Updater<string>;
|
||||
|
||||
readonly setUser: Updater<Store.User | null>;
|
||||
}
|
||||
|
||||
let store: Store = Object.freeze<Store>({
|
||||
loginUsername: "",
|
||||
loginPassword: "",
|
||||
|
||||
user: null,
|
||||
|
||||
setLoginUsername: (action) => set(mapProp("loginUsername", action)),
|
||||
setLoginPassword: (action) => set(mapProp("loginPassword", action)),
|
||||
|
||||
setUser: (action) => set(mapProp("user", action)),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
import { client } from "@/client";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useStore } from "@/hooks/useStore";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { FormEventHandler, useId, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
export function Login() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const loginUsername = useStore(state => state.loginUsername);
|
||||
const loginPassword = useStore(state => state.loginPassword);
|
||||
|
||||
const setLoginUsername = useStore(state => state.setLoginUsername);
|
||||
const setLoginPassword = useStore(state => state.setLoginPassword);
|
||||
|
||||
const setUser = useStore(state => state.setUser);
|
||||
|
||||
const usernameId = useId();
|
||||
const passwordId = useId();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const onSubmit: FormEventHandler<HTMLFormElement> = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const { data, error } = await client.login.post({
|
||||
username: loginUsername,
|
||||
password: loginPassword,
|
||||
});
|
||||
|
||||
if (error !== null) {
|
||||
console.error(error.value);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoginUsername("");
|
||||
setLoginPassword("");
|
||||
setUser(data);
|
||||
|
||||
navigate("/");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<form onSubmit={onSubmit}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Repozytorium muzyczne</CardTitle>
|
||||
<CardDescription>Zaloguj się, aby kontynuować</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Label htmlFor={usernameId}>Nazwa użytkownika</Label>
|
||||
<Input
|
||||
id={usernameId}
|
||||
className="w-[32ch]"
|
||||
type="text"
|
||||
autoComplete="username"
|
||||
value={loginUsername}
|
||||
autoFocus
|
||||
required
|
||||
onChange={(e) => setLoginUsername(e.target.value)}
|
||||
/>
|
||||
<Label htmlFor={passwordId}>Hasło</Label>
|
||||
<Input
|
||||
id={passwordId}
|
||||
className="w-[32ch]"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
value={loginPassword}
|
||||
required
|
||||
onChange={(e) => setLoginPassword(e.target.value)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button type="submit" className="w-full" disabled={isLoading}>
|
||||
{isLoading && <Loader2 className="animate-spin" />}
|
||||
Zaloguj się
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
import { client } from "@/client";
|
||||
import { API_URL_PREFIX, client } from "@/client";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { useStore } from "@/hooks/useStore";
|
||||
import { LogOut, Settings, User } from "lucide-react";
|
||||
import { Settings, User } from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
import { Link, Outlet, useNavigate } from "react-router-dom";
|
||||
import { Link, Outlet } from "react-router-dom";
|
||||
|
||||
export function Root() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const user = useStore(state => state.user);
|
||||
const setUser = useStore(state => state.setUser);
|
||||
|
||||
@@ -19,7 +17,7 @@ export function Root() {
|
||||
const { data, error } = await client.me.get();
|
||||
|
||||
if (error !== null) {
|
||||
navigate("/login");
|
||||
window.location.href = `${API_URL_PREFIX}/api/v1/login`;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,17 +27,6 @@ export function Root() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(() => void init(), []);
|
||||
|
||||
const onLogoutClick = async () => {
|
||||
const { error } = await client.logout.post();
|
||||
|
||||
if (error !== null) {
|
||||
console.error(error.value);
|
||||
}
|
||||
|
||||
setUser(null);
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
if (user === null) {
|
||||
return (
|
||||
<div className="w-full h-full overflow-hidden flex items-center justify-center">
|
||||
@@ -66,9 +53,6 @@ export function Root() {
|
||||
<Settings />Ustawienia
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={onLogoutClick}>
|
||||
<LogOut />Wyloguj się
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
@@ -1,100 +1,6 @@
|
||||
import { client } from "@/client";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useStore } from "@/hooks/useStore";
|
||||
import { Label } from "@radix-ui/react-label";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { FormEventHandler, useId, useState } from "react";
|
||||
|
||||
export function Settings() {
|
||||
return (
|
||||
<div className="p-4 overflow-y-auto grow flex flex-wrap items-start gap-4">
|
||||
<PasswordChangeCard />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PasswordChangeCard() {
|
||||
|
||||
const [currentPassword, setCurrentPassword] = useState("");
|
||||
const [newPassword1, setNewPassword1] = useState("");
|
||||
const [newPassword2, setNewPassword2] = useState("");
|
||||
|
||||
const currentPasswordId = useId();
|
||||
const newPassword1Id = useId();
|
||||
const newPassword2Id = useId();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const user = useStore(store => store.user);
|
||||
|
||||
const onSubmit: FormEventHandler<HTMLFormElement> = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const { error } = await client["change-password"].post({
|
||||
username: user!.username,
|
||||
currentPassword,
|
||||
newPassword: newPassword1,
|
||||
});
|
||||
|
||||
if (error !== null) {
|
||||
console.error(error.value);
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={onSubmit}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Zmiana hasła</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Label htmlFor={currentPasswordId}>Obecne hasło</Label>
|
||||
<Input
|
||||
id={currentPasswordId}
|
||||
className="w-[32ch]"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
value={currentPassword}
|
||||
required
|
||||
onChange={(e) => setCurrentPassword(e.target.value)}
|
||||
/>
|
||||
<Label htmlFor={currentPasswordId}>Nowe hasło</Label>
|
||||
<Input
|
||||
id={newPassword1Id}
|
||||
className="w-[32ch]"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
value={newPassword1}
|
||||
required
|
||||
onChange={(e) => setNewPassword1(e.target.value)}
|
||||
/>
|
||||
<Label htmlFor={currentPasswordId}>Potwierdź hasło</Label>
|
||||
<Input
|
||||
id={newPassword2Id}
|
||||
className="w-[32ch]"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
value={newPassword2}
|
||||
required
|
||||
onChange={(e) => setNewPassword2(e.target.value)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-row justify-end">
|
||||
<Button type="submit" disabled={isLoading || newPassword1 !== newPassword2}>
|
||||
{isLoading && <Loader2 className="animate-spin" />}
|
||||
Zmień hasło
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export function created({ createdAt, createdBy }: SystemInformation): ReactNode
|
||||
|
||||
if (Option.isSome(createdBy)) {
|
||||
nodes.push(<br />);
|
||||
nodes.push(`przez ${createdBy.value.username}`);
|
||||
nodes.push(`przez ${createdBy.value.displayName}`);
|
||||
}
|
||||
|
||||
return nodes;
|
||||
@@ -48,7 +48,7 @@ export function modified({ modifiedAt, modifiedBy }: SystemInformation): ReactNo
|
||||
if (Option.isNone(modifiedBy)) {
|
||||
return "\u2014";
|
||||
} else {
|
||||
return `przez ${modifiedBy.value.username}`;
|
||||
return `przez ${modifiedBy.value.displayName}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export function modified({ modifiedAt, modifiedBy }: SystemInformation): ReactNo
|
||||
|
||||
if (Option.isSome(modifiedBy)) {
|
||||
nodes.push(<br />);
|
||||
nodes.push(`przez ${modifiedBy.value.username}`);
|
||||
nodes.push(`przez ${modifiedBy.value.displayName}`);
|
||||
}
|
||||
|
||||
return nodes;
|
||||
|
||||
Reference in New Issue
Block a user