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
backend/modules/user
frontend/app

View file

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

View file

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

View file

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

View file

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

View file

@ -24,8 +24,8 @@ export const ReleaseCard = (props) => {
<h6>{`${props.title.substring(0, 30)}${
[...props.title].length > 30 ? "..." : ""
}`}</h6>
<p>{`${props.description}${
[...props.description].length > 160 ? "..." : ""
<p>{`${props.description.substring(0, 150)}${
[...props.description].length > 150 ? "..." : ""
}`}</p>
</div>
</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() {
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,
user: null,
token: null,
login: (user, token) => {
login: (user, token, user_id) => {
set({ isAuth: true, user, token });
setJWT(token);
setJWT(token, user_id);
},
logout: () => {
set({ isAuth: false, user: null, token: null });
removeJWT();
},
checkAuth: async (user_id) => {
checkAuth: async () => {
const jwt = getJWT();
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) {
get().login(me, jwt);
get().login(me, jwt.jwt, jwt.user_id);
} else {
get().logout();
}