Добавление временной авторизации

This commit is contained in:
2025-12-22 22:25:51 +03:00
parent 49d1681bcb
commit 82d298effe
13 changed files with 154 additions and 127 deletions
+27 -13
View File
@@ -1,6 +1,6 @@
$(() => {
$("#login-tab").on("click", function () {
$("#login-tab")
$(this)
.removeClass("text-gray-400 hover:text-gray-600")
.addClass("text-gray-700 bg-gray-50 border-b-2 border-gray-500");
$("#register-tab")
@@ -12,7 +12,7 @@ $(() => {
});
$("#register-tab").on("click", function () {
$("#register-tab")
$(this)
.removeClass("text-gray-400 hover:text-gray-600")
.addClass("text-gray-700 bg-gray-50 border-b-2 border-gray-500");
$("#login-tab")
@@ -23,6 +23,15 @@ $(() => {
$("#login-form").addClass("hidden");
});
$("body").on("click", ".toggle-password", function () {
const $btn = $(this);
const $input = $btn.siblings("input");
const isPassword = $input.attr("type") === "password";
$input.attr("type", isPassword ? "text" : "password");
$btn.find("svg").toggleClass("hidden");
});
$("#register-password").on("input", function () {
const password = $(this).val();
let strength = 0;
@@ -74,6 +83,7 @@ $(() => {
const username = $("#login-username").val();
const password = $("#login-password").val();
const rememberMe = $("#remember-me").prop("checked");
$submitBtn.prop("disabled", true).text("Вход...");
try {
@@ -82,10 +92,17 @@ $(() => {
formData.append("password", password);
const data = await Api.postForm("/api/auth/token", formData);
const storage = rememberMe ? localStorage : sessionStorage;
storage.setItem("access_token", data.access_token);
if (rememberMe && data.refresh_token) {
storage.setItem("refresh_token", data.refresh_token);
}
const otherStorage = rememberMe ? sessionStorage : localStorage;
otherStorage.removeItem("access_token");
otherStorage.removeItem("refresh_token");
localStorage.setItem("access_token", data.access_token);
if (data.refresh_token)
localStorage.setItem("refresh_token", data.refresh_token);
window.location.href = "/";
} catch (error) {
Utils.showToast(error.message || "Ошибка входа", "error");
@@ -117,7 +134,11 @@ $(() => {
try {
await Api.post("/api/auth/register", userData);
Utils.showToast("Регистрация успешна! Войдите в систему.", "success");
setTimeout(() => window.location.reload(), 1500);
setTimeout(() => {
$("#login-tab").trigger("click");
$("#login-username").val(userData.username);
}, 1500);
} catch (error) {
let msg = error.message;
if (Array.isArray(error.detail)) {
@@ -128,11 +149,4 @@ $(() => {
$submitBtn.prop("disabled", false).text("Зарегистрироваться");
}
});
$("body").on("click", ".toggle-password", function () {
const $input = $(this).siblings("input");
const type = $input.attr("type") === "password" ? "text" : "password";
$input.attr("type", type);
$(this).find("svg").toggleClass("hidden");
});
});
+1 -1
View File
@@ -12,7 +12,7 @@ $(document).ready(() => {
document.title = `LiB - ${author.name}`;
renderAuthor(author);
renderBooks(author.books);
if (window.canManage) {
if (window.canManage()) {
$("#edit-author-btn")
.attr("href", `/author/${author.id}/edit`)
.removeClass("hidden");
+2 -2
View File
@@ -57,7 +57,7 @@ $(document).ready(() => {
currentBook = book;
document.title = `LiB - ${book.title}`;
renderBook(book);
if (window.canManage) {
if (window.canManage()) {
$("#edit-book-btn")
.attr("href", `/book/${book.id}/edit`)
.removeClass("hidden");
@@ -123,7 +123,7 @@ $(document).ready(() => {
$container.empty();
const config = getStatusConfig(book.status);
if (window.canManage) {
if (window.canManage()) {
const $dropdownHTML = $(`
<div class="relative inline-block text-left w-full md:w-auto">
<button id="status-toggle-btn" type="button" class="w-full justify-center md:w-auto inline-flex items-center px-4 py-2 rounded-full text-sm font-semibold transition-all shadow-sm ${config.bgClass} ${config.textClass} hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-400">
+1 -1
View File
@@ -403,7 +403,7 @@ $(document).ready(() => {
});
function showAdminControls() {
if (window.canManage) {
if (window.canManage()) {
$("#admin-actions").removeClass("hidden");
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
$(document).ready(() => {
if (!window.canManage) return;
if (!window.canManage()) return;
setTimeout(() => window.canManage, 100);
const $form = $("#create-author-form");
+1 -1
View File
@@ -1,5 +1,5 @@
$(document).ready(() => {
if (!window.canManage) return;
if (!window.canManage()) return;
setTimeout(() => window.canManage, 100);
let allAuthors = [];
+1 -1
View File
@@ -1,5 +1,5 @@
$(document).ready(() => {
if (!window.canManage) return;
if (!window.canManage()) return;
setTimeout(() => window.canManage, 100);
const $form = $("#create-genre-form");
+1 -1
View File
@@ -1,5 +1,5 @@
$(document).ready(() => {
if (!window.canManage) return;
if (!window.canManage()) return;
setTimeout(() => window.canManage, 100);
const pathParts = window.location.pathname.split("/");
+1 -1
View File
@@ -1,5 +1,5 @@
$(document).ready(() => {
if (!window.canManage) {
if (!window.canManage()) {
Utils.showToast("У вас недостаточно прав", "error");
setTimeout(() => (window.location.href = "/"), 1500);
return;
+1 -1
View File
@@ -1,5 +1,5 @@
$(document).ready(() => {
const token = localStorage.getItem("access_token");
const token = StorageHelper.get("access_token");
if (!token) {
window.location.href = "/auth";
return;
+72 -83
View File
@@ -1,11 +1,74 @@
@keyframes shake {
0%,
to {
transform: translateX(0);
}
10%,
30%,
50%,
70%,
90% {
transform: translateX(-5px);
}
20%,
40%,
60%,
80% {
transform: translateX(5px);
}
}
@keyframes fadeIn {
0% {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes dropdownFade {
0% {
opacity: 0;
transform: translateY(-5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse-soft {
0%,
to {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
@keyframes fadeInUp {
0% {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@font-face {
font-family: "Novem";
src: url("novem.regular.ttf") format("truetype");
src: url(novem.regular.ttf) format("truetype");
}
@font-face {
font-family: "Dited";
src: url("dited.regular.ttf") format("truetype");
src: url(dited.regular.ttf) format("truetype");
}
h1 {
@@ -13,9 +76,9 @@ h1 {
letter-spacing: 10px;
}
h2,
.book-id,
.book-status,
h2,
nav ul li a {
font-family: "Dited", sans-serif;
letter-spacing: 2.5px;
@@ -73,7 +136,7 @@ nav ul li a {
top: 6px;
width: 4px;
height: 8px;
border: solid white;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
@@ -96,17 +159,6 @@ button:disabled {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.flex.justify-center.gap-4 button:hover {
transform: translateY(-2px);
}
@@ -115,30 +167,10 @@ button:disabled {
animation: shake 0.5s ease-in-out;
}
@keyframes shake {
0%,
100% {
transform: translateX(0);
}
10%,
30%,
50%,
70%,
90% {
transform: translateX(-5px);
}
20%,
40%,
60%,
80% {
transform: translateX(5px);
}
}
#req-digit,
#req-length,
#req-upper,
#req-lower,
#req-digit {
#req-upper {
transition: color 0.2s ease;
}
@@ -153,17 +185,6 @@ button:disabled {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
#login-tab,
#register-tab {
font-family: "Dited", sans-serif;
@@ -175,17 +196,6 @@ button:disabled {
animation: dropdownFade 0.1s ease-out;
}
@keyframes dropdownFade {
from {
opacity: 0;
transform: translateY(-5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
#user-arrow.rotate-180 {
transform: rotate(180deg);
}
@@ -198,16 +208,6 @@ button:disabled {
min-width: 140px;
}
@keyframes pulse-soft {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
.animate-pulse-soft {
animation: pulse-soft 2s ease-in-out infinite;
}
@@ -216,32 +216,21 @@ button:disabled {
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1));
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-up {
animation: fadeInUp 0.5s ease-out forwards;
}
.stat-card:hover svg {
transform: scale(1.1);
transition: transform 0.3s ease;
}
.stat-card svg {
.stat-card svg,
.stat-card:hover svg {
transition: transform 0.3s ease;
}
.gradient-text {
background: linear-gradient(135deg, #374151 0%, #6b7280 100%);
background: linear-gradient(135deg, #374151 0, #6b7280 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
+45 -18
View File
@@ -1,3 +1,22 @@
const StorageHelper = {
get: (key) => {
return localStorage.getItem(key) || sessionStorage.getItem(key);
},
getCurrentStorage: () => {
return localStorage.getItem("refresh_token")
? localStorage
: sessionStorage;
},
clearAll: () => {
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
localStorage.removeItem("user");
sessionStorage.removeItem("access_token");
sessionStorage.removeItem("refresh_token");
sessionStorage.removeItem("user");
},
};
const Utils = {
escapeHtml: (text) => {
if (!text) return "";
@@ -59,7 +78,8 @@ const Api = {
async request(endpoint, options = {}) {
const fullUrl = this.getBaseUrl() + endpoint;
const token = localStorage.getItem("access_token");
const token = StorageHelper.get("access_token");
const headers = {
"Content-Type": "application/json",
...options.headers,
@@ -74,11 +94,13 @@ const Api = {
try {
const response = await fetch(fullUrl, config);
if (response.status === 401) {
const isLoginRequest = endpoint.includes("/auth/token");
if (response.status === 401 && !isLoginRequest) {
const refreshed = await Auth.tryRefresh();
if (refreshed) {
headers["Authorization"] =
`Bearer ${localStorage.getItem("access_token")}`;
`Bearer ${StorageHelper.get("access_token")}`;
const retryResponse = await fetch(fullUrl, { ...options, headers });
if (retryResponse.ok) {
return retryResponse.json();
@@ -90,7 +112,11 @@ const Api = {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || `Error ${response.status}`);
throw new Error(
errorData.detail ||
errorData.error_description ||
`Ошибка ${response.status}`,
);
}
return response.json();
} catch (error) {
@@ -131,16 +157,16 @@ const Api = {
const Auth = {
logout: () => {
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
localStorage.removeItem("user");
StorageHelper.clearAll();
window.location.href = "/";
},
tryRefresh: async () => {
const refreshToken = localStorage.getItem("refresh_token");
const refreshToken = StorageHelper.get("refresh_token");
if (!refreshToken) return false;
const activeStorage = StorageHelper.getCurrentStorage();
try {
const response = await fetch("/api/auth/refresh", {
method: "POST",
@@ -150,8 +176,8 @@ const Auth = {
if (response.ok) {
const data = await response.json();
localStorage.setItem("access_token", data.access_token);
localStorage.setItem("refresh_token", data.refresh_token);
activeStorage.setItem("access_token", data.access_token);
activeStorage.setItem("refresh_token", data.refresh_token);
return true;
}
} catch (e) {
@@ -161,14 +187,17 @@ const Auth = {
},
init: async () => {
const token = localStorage.getItem("access_token");
const refreshToken = localStorage.getItem("refresh_token");
const token = StorageHelper.get("access_token");
const refreshToken = StorageHelper.get("refresh_token");
if (!token && !refreshToken) {
localStorage.removeItem("user");
sessionStorage.removeItem("user");
return null;
}
const activeStorage = StorageHelper.getCurrentStorage();
try {
let response = await fetch("/api/auth/me", {
headers: { Authorization: "Bearer " + token },
@@ -179,7 +208,7 @@ const Auth = {
if (refreshed) {
response = await fetch("/api/auth/me", {
headers: {
Authorization: "Bearer " + localStorage.getItem("access_token"),
Authorization: "Bearer " + StorageHelper.get("access_token"),
},
});
}
@@ -187,7 +216,7 @@ const Auth = {
if (response.ok) {
const user = await response.json();
localStorage.setItem("user", JSON.stringify(user));
activeStorage.setItem("user", JSON.stringify(user));
document.dispatchEvent(new CustomEvent("auth:login", { detail: user }));
return user;
}
@@ -195,15 +224,13 @@ const Auth = {
console.error("Auth check failed", e);
}
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
localStorage.removeItem("user");
StorageHelper.clearAll();
return null;
},
};
window.getUser = function () {
const userJson = localStorage.getItem("user");
const userJson = StorageHelper.get("user");
if (!userJson) return null;
try {
return JSON.parse(userJson);