mirror of
https://github.com/idanoo/autobrr
synced 2025-07-25 17:59:14 +00:00
Feature: Auth (#4)
* feat(api): add auth * feat(web): add auth and refactor * refactor(web): baseurl * feat: add autobrrctl cli for user creation * build: move static assets * refactor(web): auth guard and routing * refactor: rename var * fix: remove subrouter * build: update default config
This commit is contained in:
parent
2e8d0950c1
commit
40b855bf39
56 changed files with 1208 additions and 257 deletions
|
@ -5,11 +5,11 @@ import {classNames} from "../styles/utils";
|
|||
import {CheckIcon, ChevronRightIcon, ExclamationIcon, SelectorIcon,} from "@heroicons/react/solid";
|
||||
import {useToggle} from "../hooks/hooks";
|
||||
import {useMutation} from "react-query";
|
||||
import {queryClient} from "..";
|
||||
import {Field, Form} from "react-final-form";
|
||||
import {TextField} from "./inputs";
|
||||
import DEBUG from "./debug";
|
||||
import APIClient from "../api/APIClient";
|
||||
import {queryClient} from "../App";
|
||||
|
||||
interface radioFieldsetOption {
|
||||
label: string;
|
||||
|
|
38
web/src/components/Layout.tsx
Normal file
38
web/src/components/Layout.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import {isLoggedIn} from "../state/state";
|
||||
import {useRecoilState} from "recoil";
|
||||
import {useEffect, useState} from "react";
|
||||
import { Fragment } from "react";
|
||||
import {Redirect} from "react-router-dom";
|
||||
import APIClient from "../api/APIClient";
|
||||
|
||||
export default function Layout({auth=false, authFallback="/login", children}: any) {
|
||||
const [loggedIn, setLoggedIn] = useRecoilState(isLoggedIn);
|
||||
const [loading, setLoading] = useState(auth);
|
||||
|
||||
useEffect(() => {
|
||||
// check token
|
||||
APIClient.auth.test()
|
||||
.then(r => {
|
||||
setLoggedIn(true);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(a => {
|
||||
setLoading(false);
|
||||
})
|
||||
|
||||
}, [setLoggedIn])
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{loading ? null : (
|
||||
<Fragment>
|
||||
{auth && !loggedIn ? <Redirect to={authFallback} /> : (
|
||||
<Fragment>
|
||||
{children}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
47
web/src/components/inputs/PasswordField.tsx
Normal file
47
web/src/components/inputs/PasswordField.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { Field } from "react-final-form";
|
||||
import React from "react";
|
||||
import Error from "./Error";
|
||||
import {classNames} from "../../styles/utils";
|
||||
|
||||
type COL_WIDTHS = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
columns?: COL_WIDTHS;
|
||||
className?: string;
|
||||
autoComplete?: string;
|
||||
}
|
||||
|
||||
const PasswordField: React.FC<Props> = ({ name, label, placeholder, columns , className, autoComplete}) => (
|
||||
<div
|
||||
className={classNames(
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
)}
|
||||
>
|
||||
{label && (
|
||||
<label htmlFor={name} className="block text-xs font-bold text-gray-700 uppercase tracking-wide">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<Field
|
||||
name={name}
|
||||
render={({input, meta}) => (
|
||||
<input
|
||||
{...input}
|
||||
id={name}
|
||||
type="password"
|
||||
autoComplete={autoComplete}
|
||||
className="mt-2 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-light-blue-500 focus:border-light-blue-500 sm:text-sm"
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div>
|
||||
<Error name={name} classNames="text-red mt-2" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default PasswordField;
|
|
@ -11,9 +11,10 @@ interface Props {
|
|||
placeholder?: string;
|
||||
columns?: COL_WIDTHS;
|
||||
className?: string;
|
||||
autoComplete?: string;
|
||||
}
|
||||
|
||||
const TextField: React.FC<Props> = ({ name, label, placeholder, columns , className}) => (
|
||||
const TextField: React.FC<Props> = ({ name, label, placeholder, columns , className, autoComplete}) => (
|
||||
<div
|
||||
className={classNames(
|
||||
columns ? `col-span-${columns}` : "col-span-12"
|
||||
|
@ -31,6 +32,7 @@ const TextField: React.FC<Props> = ({ name, label, placeholder, columns , classN
|
|||
{...input}
|
||||
id={name}
|
||||
type="text"
|
||||
autoComplete={autoComplete}
|
||||
className="mt-2 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-light-blue-500 focus:border-light-blue-500 sm:text-sm"
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export { default as TextField } from "./TextField";
|
||||
export { default as TextFieldWide } from "./TextFieldWide";
|
||||
export { default as PasswordField } from "./PasswordField";
|
||||
export { default as TextAreaWide } from "./TextAreaWide";
|
||||
export { default as MultiSelectField } from "./MultiSelectField";
|
||||
export { default as RadioFieldset } from "./RadioFieldset";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue