mirror of
https://github.com/Radiquum/AniX.git
synced 2025-04-07 00:34:41 +00:00
feat: add authorization!
This commit is contained in:
parent
f3667eb209
commit
3a800a4933
7 changed files with 248 additions and 1 deletions
21
app/api/profile/[id]/route.js
Normal file
21
app/api/profile/[id]/route.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { fetchDataViaGet } from "@/app/api/utils";
|
||||||
|
import { ENDPOINTS } from "@/app/api/config";
|
||||||
|
|
||||||
|
export async function GET(request, params) {
|
||||||
|
const token = request.nextUrl.searchParams.get(["token"]) || null;
|
||||||
|
let url = new URL(`${ENDPOINTS.profile}/${params["params"]["id"]}`);
|
||||||
|
if (token) {
|
||||||
|
url.searchParams.set("token", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetchDataViaGet(url.toString());
|
||||||
|
if (!response) {
|
||||||
|
return NextResponse.json({ message: "Server Error" }, { status: 500 });
|
||||||
|
}
|
||||||
|
if (!response.profile) {
|
||||||
|
return NextResponse.json({ message: "Profile not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json(response);
|
||||||
|
}
|
15
app/api/profile/login/route.js
Normal file
15
app/api/profile/login/route.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { authorize } from "@/app/api/utils";
|
||||||
|
import { ENDPOINTS } from "@/app/api/config";
|
||||||
|
|
||||||
|
export async function POST(request) {
|
||||||
|
const response = await authorize(ENDPOINTS.auth, await request.json());
|
||||||
|
if (!response) {
|
||||||
|
return NextResponse.json({ message: "Server Error" }, { status: 500 });
|
||||||
|
}
|
||||||
|
if (!response.profile) {
|
||||||
|
return NextResponse.json({ message: "Profile not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json(response);
|
||||||
|
}
|
|
@ -6,11 +6,11 @@ export async function GET(request) {
|
||||||
const page = parseInt(request.nextUrl.searchParams.get(["page"])) || 0;
|
const page = parseInt(request.nextUrl.searchParams.get(["page"])) || 0;
|
||||||
const query = request.nextUrl.searchParams.get(["q"]) || null;
|
const query = request.nextUrl.searchParams.get(["q"]) || null;
|
||||||
const token = request.nextUrl.searchParams.get(["token"]) || null;
|
const token = request.nextUrl.searchParams.get(["token"]) || null;
|
||||||
|
let url = new URL(`${ENDPOINTS.search}/${page}`);
|
||||||
if (token) {
|
if (token) {
|
||||||
url.searchParams.set("token", token);
|
url.searchParams.set("token", token);
|
||||||
}
|
}
|
||||||
const data = { query, searchBy: 0 };
|
const data = { query, searchBy: 0 };
|
||||||
let url = new URL(`${ENDPOINTS.search}/${page}`);
|
|
||||||
|
|
||||||
const response = await fetchDataViaPost(url.toString(), data);
|
const response = await fetchDataViaPost(url.toString(), data);
|
||||||
if (!response) {
|
if (!response) {
|
||||||
|
|
|
@ -9,6 +9,9 @@ export const fetchDataViaGet = async (url) => {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
headers: HEADERS,
|
headers: HEADERS,
|
||||||
});
|
});
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error("Error fetching data");
|
||||||
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -23,9 +26,43 @@ export const fetchDataViaPost = async (url, body) => {
|
||||||
headers: HEADERS,
|
headers: HEADERS,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error("Error fetching data");
|
||||||
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const authorize = async (url, data) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${url}?login=${data.login}&password=${data.password}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"User-Agent": USER_AGENT,
|
||||||
|
Sign: "9aa5c7af74e8cd70c86f7f9587bde23d",
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error("Error authorizing user");
|
||||||
|
}
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export function setJWT(user_id, jwt) {
|
||||||
|
const data = { jwt: jwt, user_id: user_id };
|
||||||
|
localStorage.setItem("JWT", JSON.stringify(data));
|
||||||
|
}
|
||||||
|
export function getJWT() {
|
||||||
|
const data = localStorage.getItem("JWT");
|
||||||
|
return JSON.parse(data);
|
||||||
|
}
|
||||||
|
export function removeJWT() {
|
||||||
|
localStorage.removeItem("JWT");
|
||||||
|
}
|
||||||
|
|
4
app/login/page.js
Normal file
4
app/login/page.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { LoginPage } from "@/app/pages/Login";
|
||||||
|
export default function Login() {
|
||||||
|
return <LoginPage />;
|
||||||
|
}
|
138
app/pages/Login.jsx
Normal file
138
app/pages/Login.jsx
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
"use client";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { useUserStore } from "@/app/store/auth";
|
||||||
|
import { setJWT } from "@/app/api/utils";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
export function LoginPage() {
|
||||||
|
const [login, setLogin] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [remember, setRemember] = useState(false);
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
function submit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
fetch("/api/profile/login", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
login: login,
|
||||||
|
password: password,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json();
|
||||||
|
} else {
|
||||||
|
alert("Ошибка получения пользователя.");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (data.profileToken) {
|
||||||
|
userStore.login(data.profile, data.profileToken.token);
|
||||||
|
if (remember) {
|
||||||
|
setJWT(data.profile.id, data.profileToken.token);
|
||||||
|
}
|
||||||
|
router.push("/");
|
||||||
|
} else {
|
||||||
|
alert("Неверные данные.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (userStore.user) {
|
||||||
|
router.push("/");
|
||||||
|
}
|
||||||
|
}, [userStore.user]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
<section className="bg-gray-50 dark:bg-gray-900">
|
||||||
|
<div className="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
|
||||||
|
<div className="w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
|
||||||
|
<div className="p-6 space-y-4 md:space-y-6 sm:p-8">
|
||||||
|
<h1 className="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white">
|
||||||
|
Вход в аккаунт Anixart
|
||||||
|
</h1>
|
||||||
|
<form
|
||||||
|
className="space-y-4 md:space-y-6"
|
||||||
|
method="POST"
|
||||||
|
onSubmit={submit}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
htmlFor="email"
|
||||||
|
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
||||||
|
>
|
||||||
|
Эл. почта
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
id="email"
|
||||||
|
className="bg-gray-50 border border-gray-300 text-gray-900 rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||||
|
placeholder="name@company.com"
|
||||||
|
value={login}
|
||||||
|
onChange={(e) => setLogin(e.target.value)}
|
||||||
|
required=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
htmlFor="password"
|
||||||
|
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
||||||
|
>
|
||||||
|
Пароль
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
id="password"
|
||||||
|
placeholder="••••••••"
|
||||||
|
className="bg-gray-50 border border-gray-300 text-gray-900 rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||||
|
required=""
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-start">
|
||||||
|
<div className="flex items-center h-5">
|
||||||
|
<input
|
||||||
|
id="remember"
|
||||||
|
aria-describedby="remember"
|
||||||
|
type="checkbox"
|
||||||
|
className="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-primary-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-primary-600 dark:ring-offset-gray-800"
|
||||||
|
required=""
|
||||||
|
value={remember}
|
||||||
|
onChange={(e) => setRemember(e.target.checked)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="ml-3 text-sm">
|
||||||
|
<label
|
||||||
|
htmlFor="remember"
|
||||||
|
className="text-gray-500 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
Запомнить вход
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full text-white bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||||
|
>
|
||||||
|
Войти
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
"use client";
|
||||||
|
import { create } from "zustand";
|
||||||
|
import { getJWT, setJWT, removeJWT, fetchDataViaGet } from "@/app/api/utils";
|
||||||
|
|
||||||
|
export const useUserStore = create((set, get) => ({
|
||||||
|
isAuth: false,
|
||||||
|
user: null,
|
||||||
|
token: null,
|
||||||
|
|
||||||
|
login: (user, token) => {
|
||||||
|
set({ isAuth: true, user, token });
|
||||||
|
},
|
||||||
|
logout: () => {
|
||||||
|
set({ isAuth: false, user: null, token: null });
|
||||||
|
removeJWT();
|
||||||
|
},
|
||||||
|
checkAuth: async () => {
|
||||||
|
const jwt = getJWT();
|
||||||
|
if (jwt) {
|
||||||
|
const data = await fetchDataViaGet(
|
||||||
|
`/api/profile/${jwt.user_id}?token=${jwt.jwt}`
|
||||||
|
);
|
||||||
|
if (data && data.is_my_profile) {
|
||||||
|
get().login(data, jwt.user_id, jwt.jwt);
|
||||||
|
} else {
|
||||||
|
get().logout();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
get().logout();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
Loading…
Add table
Reference in a new issue