Back and Front: User Login

This commit is contained in:
Kentai Radiquum 2024-04-23 23:59:24 +05:00
parent a03deddbc0
commit 32a4da1a31
Signed by: Radiquum
GPG key ID: 858E8EE696525EED
8 changed files with 141 additions and 36 deletions

View file

@ -1,20 +1,21 @@
from typing import Annotated
import requests import requests
from fastapi import APIRouter from fastapi import APIRouter
from fastapi import Form
from fastapi import Request
from modules.proxy import ENDPOINTS from modules.proxy import ENDPOINTS
from modules.proxy import USER_AGENT from modules.proxy import USER_AGENT
from pydantic import BaseModel
class User(BaseModel):
email: str
password: str
router = APIRouter() router = APIRouter()
@router.post("", summary="logging in") @router.post("", summary="logging in")
async def userSignIn( async def userSignIn(
request: Request, user: User,
email: Annotated[str, Form()],
password: Annotated[str, Form()],
short: bool = False, short: bool = False,
): ):
headers = { headers = {
@ -26,7 +27,7 @@ async def userSignIn(
# noqa: E501 # noqa: E501
f"{ENDPOINTS['auth']}", f"{ENDPOINTS['auth']}",
headers=headers, headers=headers,
data={"login": email, "password": password}, data={"login": user.email, "password": user.password},
) )
if r.status_code != 200: if r.status_code != 200:
return {"error": r.text} return {"error": r.text}

View file

@ -5,23 +5,25 @@ import "material-dynamic-colors";
import { NavigationRail } from "@/app/components/NavigationRail/NavigationRail"; import { NavigationRail } from "@/app/components/NavigationRail/NavigationRail";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { ColorPicker } from "@/app/components/ColorPicker/ColorPicker"; import { ColorPicker } from "@/app/components/ColorPicker/ColorPicker";
import { useUserStore } from "./store/user-store";
export function setMode(mode) { function setMode(mode) {
localStorage.setItem("mode", mode); localStorage.setItem("mode", mode);
} }
export function getMode() { function getMode() {
return localStorage.getItem("mode"); return localStorage.getItem("mode");
} }
export function setTheme(theme) { function setTheme(theme) {
localStorage.setItem("theme", theme); localStorage.setItem("theme", theme);
} }
export function getTheme() { function getTheme() {
return localStorage.getItem("theme"); return localStorage.getItem("theme");
} }
export const App = (props) => { export const App = (props) => {
const [colorPicker, setColorPicker] = useState(false); const [colorPicker, setColorPicker] = useState(false);
const userStore = useUserStore();
const theme = async (from) => { const theme = async (from) => {
setTheme(from); setTheme(from);
@ -45,6 +47,11 @@ export const App = (props) => {
} }
}, []); }, []);
useEffect(() => {
userStore.checkAuth();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return ( return (
<body> <body>
<div> <div>

View file

@ -1,3 +1,7 @@
export const isResponseOk = (response) => {
return !(response instanceof Error);
};
export const getData = async (url) => { export const getData = async (url) => {
try { try {
const response = await fetch(url); const response = await fetch(url);
@ -14,8 +18,8 @@ export const authorize = async (url, data) => {
try { try {
const response = await fetch(url, { const response = await fetch(url, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" }, headers: { "Content-Type": "application/json" },
body: data, body: JSON.stringify(data),
}); });
if (response.status !== 200) { if (response.status !== 200) {
throw new Error("Ошибка получения данных"); throw new Error("Ошибка получения данных");
@ -28,7 +32,7 @@ export const authorize = async (url, data) => {
export const getMe = async (url, jwt) => { export const getMe = async (url, jwt) => {
try { try {
const response = await fetch(`${url}?token=${jwt}&short=True`, { const response = await fetch(`${url}?token=${jwt}`, {
method: "GET", method: "GET",
}); });
if (response.status !== 200) { if (response.status !== 200) {
@ -40,12 +44,14 @@ export const getMe = async (url, jwt) => {
} }
}; };
export function setJWT(jwt) { export function setJWT(jwt, user_id) {
localStorage.setItem("jwt", jwt); const data = { jwt: jwt, user_id: user_id };
localStorage.setItem("data", JSON.stringify(data));
} }
export function getJWT() { export function getJWT() {
return localStorage.getItem("jwt"); const data = localStorage.getItem("data");
return JSON.parse(data);
} }
export function removeJWT() { export function removeJWT() {
localStorage.removeItem("jwt"); localStorage.removeItem("data");
} }

View file

@ -8,4 +8,8 @@ export const endpoints = {
finished: `${API_URL}/index/finished`, finished: `${API_URL}/index/finished`,
}, },
search: `${API_URL}/search`, search: `${API_URL}/search`,
user: {
profile: `${API_URL}/profile`,
auth: `${API_URL}/auth`,
},
}; };

View file

@ -3,9 +3,13 @@
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { useUserStore } from "@/app/store/user-store";
import { useRouter } from "next/navigation";
export const NavigationRail = (props) => { export const NavigationRail = (props) => {
const pathname = usePathname(); const pathname = usePathname();
const userStore = useUserStore();
const router = useRouter();
const items = [ const items = [
{ {
@ -37,15 +41,26 @@ export const NavigationRail = (props) => {
return ( return (
<nav className="left"> <nav className="left">
<button className="circle transparent "> {userStore.isAuth && userStore.user ? (
<Image <Link className="circle transparent " href="/profile">
className="responsive" <Image
src="/favicon.ico" className="responsive"
alt="Ваш профиль" src={userStore.user.profile.avatar}
width="64" alt="Ваш профиль"
height="64" width="64"
/> height="64"
</button> />
</Link>
) : (
<button
className="circle transparent"
onClick={() => {
router.push("/login");
}}
>
<i className="responsive">login</i>
</button>
)}
{items.map((item) => { {items.map((item) => {
return ( return (
@ -67,6 +82,17 @@ export const NavigationRail = (props) => {
> >
<i>palette</i> <i>palette</i>
</button> </button>
{userStore.isAuth ? (
<button
className="circle transparent"
onClick={() => userStore.logout()}
>
<i>logout</i>
</button>
) : (
""
)}
</nav> </nav>
); );
}; };

View file

@ -24,8 +24,8 @@ export const ReleaseCard = (props) => {
<h6>{`${props.title.substring(0, 30)}${ <h6>{`${props.title.substring(0, 30)}${
[...props.title].length > 30 ? "..." : "" [...props.title].length > 30 ? "..." : ""
}`}</h6> }`}</h6>
<p>{`${props.description}${ <p>{`${props.description.substring(0, 150)}${
[...props.description].length > 160 ? "..." : "" [...props.description].length > 150 ? "..." : ""
}`}</p> }`}</p>
</div> </div>
</article> </article>

View file

@ -1,3 +1,60 @@
"use client";
import { useUserStore } from "../store/user-store";
import { endpoints } from "../api/config";
import { authorize, isResponseOk } from "../api/api-utils";
import { useRouter } from "next/navigation";
import { useState, useEffect } from "react";
export default function LoginPage() { export default function LoginPage() {
return <p>login page</p>; const userStore = useUserStore();
const router = useRouter();
const [authData, setAuthData] = useState({ email: "", password: "" });
const handleInput = (e) => {
setAuthData({ ...authData, [e.target.name]: e.target.value });
};
const handleSubmit = async (e) => {
e.preventDefault();
const userData = await authorize(endpoints.user.auth, authData);
if (isResponseOk(userData)) {
userStore.login(
userData,
userData.profileToken.token,
userData.profile.id,
);
}
};
useEffect(() => {
let timer;
if (userStore.user) {
timer = setTimeout(() => {
router.push("/profile");
}, 1000);
}
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [userStore.user]);
return (
<div className="absolute padding tertiary center middle round">
<i className="extra">login</i>
<h5>Вход в аккаунт anixart.</h5>
<form onSubmit={handleSubmit}>
<div className="border field fill label large round">
<input type="email" name="email" onInput={handleInput} />
<label>логин</label>
</div>
<div className="border field fill label large round">
<input type="password" name="password" onInput={handleInput} />
<label>пароль</label>
</div>
<button className="small-round medium" type="submit">
<i>login</i>
<span>Войти</span>
</button>
</form>
</div>
);
} }

View file

@ -7,20 +7,24 @@ export const useUserStore = create((set, get) => ({
isAuth: false, isAuth: false,
user: null, user: null,
token: null, token: null,
login: (user, token) => {
login: (user, token, user_id) => {
set({ isAuth: true, user, token }); set({ isAuth: true, user, token });
setJWT(token); setJWT(token, user_id);
}, },
logout: () => { logout: () => {
set({ isAuth: false, user: null, token: null }); set({ isAuth: false, user: null, token: null });
removeJWT(); removeJWT();
}, },
checkAuth: async (user_id) => { checkAuth: async () => {
const jwt = getJWT(); const jwt = getJWT();
if (jwt) { if (jwt) {
const me = await getMe(`${endpoints.profile}/${user_id}`, jwt); const me = await getMe(
`${endpoints.user.profile}/${jwt.user_id}`,
jwt.jwt,
);
if (me.is_my_profile) { if (me.is_my_profile) {
get().login(me, jwt); get().login(me, jwt.jwt, jwt.user_id);
} else { } else {
get().logout(); get().logout();
} }