Обновлены функции поиска заметок для регистронезависимого поиска по содержимому. Добавлен новый API для выхода, который логирует действие и очищает сессию. Реализована очистка IndexedDB при разлогинивании и смене пользователя. Обновлены компоненты для управления состоянием аутентификации и очистки данных при регистрации и удалении аккаунта.
This commit is contained in:
parent
80c42f8df0
commit
7367364d93
@ -857,10 +857,10 @@ app.get("/api/notes/search", requireApiAuth, (req, res) => {
|
||||
let whereClause = "WHERE n.user_id = ? AND n.is_archived = 0";
|
||||
let params = [req.session.userId];
|
||||
|
||||
// Поиск по тексту
|
||||
// Поиск по тексту (регистронезависимый)
|
||||
if (q && q.trim()) {
|
||||
whereClause += " AND n.content LIKE ?";
|
||||
params.push(`%${q.trim()}%`);
|
||||
whereClause += " AND LOWER(n.content) LIKE ?";
|
||||
params.push(`%${q.trim().toLowerCase()}%`);
|
||||
}
|
||||
|
||||
// Поиск по тегу (регистронезависимый)
|
||||
@ -2549,6 +2549,23 @@ app.post("/logout", (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
// API для выхода (для фронтенда)
|
||||
app.post("/api/logout", (req, res) => {
|
||||
const userId = req.session.userId;
|
||||
|
||||
// Логируем выход
|
||||
if (userId) {
|
||||
logAction(userId, "logout", "Выход из системы");
|
||||
}
|
||||
|
||||
req.session.destroy((err) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: "Ошибка выхода" });
|
||||
}
|
||||
res.json({ success: true, message: "Выход выполнен успешно" });
|
||||
});
|
||||
});
|
||||
|
||||
// API для удаления аккаунта
|
||||
app.delete("/api/user/delete-account", requireApiAuth, async (req, res) => {
|
||||
const { password } = req.body;
|
||||
|
||||
@ -82,7 +82,7 @@ define(['./workbox-9dc17825'], (function (workbox) { 'use strict';
|
||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||
}, {
|
||||
"url": "index.html",
|
||||
"revision": "0.2vg2p27g3bg"
|
||||
"revision": "0.mib567t8mr8"
|
||||
}], {});
|
||||
workbox.cleanupOutdatedCaches();
|
||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import axios from "axios";
|
||||
import { dbManager } from "../utils/indexedDB";
|
||||
|
||||
const axiosClient = axios.create({
|
||||
baseURL: "/api",
|
||||
@ -48,6 +49,10 @@ axiosClient.interceptors.response.use(
|
||||
|
||||
// Разлогиниваем только если это НЕ запрос с проверкой пароля
|
||||
if (!isPasswordProtected) {
|
||||
// Очищаем IndexedDB при автоматическом разлогинивании
|
||||
dbManager.clearAll().catch((err) => {
|
||||
console.error("Ошибка очистки IndexedDB при 401:", err);
|
||||
});
|
||||
localStorage.removeItem("isAuthenticated");
|
||||
window.location.href = "/";
|
||||
}
|
||||
|
||||
@ -4,11 +4,13 @@ import { useAppSelector, useAppDispatch } from "../store/hooks";
|
||||
import { setAuth, clearAuth } from "../store/slices/authSlice";
|
||||
import { authApi } from "../api/authApi";
|
||||
import { LoadingOverlay } from "./common/LoadingOverlay";
|
||||
import { dbManager } from "../utils/indexedDB";
|
||||
|
||||
export const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const isAuthenticated = useAppSelector((state) => state.auth.isAuthenticated);
|
||||
const currentUserId = useAppSelector((state) => state.auth.userId);
|
||||
const dispatch = useAppDispatch();
|
||||
const [isChecking, setIsChecking] = useState(true);
|
||||
|
||||
@ -17,9 +19,17 @@ export const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({
|
||||
try {
|
||||
const authStatus = await authApi.checkStatus();
|
||||
if (authStatus.authenticated) {
|
||||
const newUserId = authStatus.userId!;
|
||||
|
||||
// Если пользователь изменился, очищаем IndexedDB
|
||||
if (currentUserId && currentUserId !== newUserId) {
|
||||
console.log(`[ProtectedRoute] User changed from ${currentUserId} to ${newUserId}, clearing IndexedDB`);
|
||||
await dbManager.clearAll();
|
||||
}
|
||||
|
||||
dispatch(
|
||||
setAuth({
|
||||
userId: authStatus.userId!,
|
||||
userId: newUserId,
|
||||
username: authStatus.username!,
|
||||
})
|
||||
);
|
||||
@ -36,7 +46,7 @@ export const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({
|
||||
// Всегда проверяем статус аутентификации при монтировании,
|
||||
// независимо от начального состояния Redux (localStorage может быть устаревшим)
|
||||
checkAuth();
|
||||
}, [dispatch]);
|
||||
}, [dispatch, currentUserId]);
|
||||
|
||||
if (isChecking) {
|
||||
return <LoadingOverlay />;
|
||||
|
||||
@ -6,6 +6,7 @@ import { authApi } from "../api/authApi";
|
||||
import { useNotification } from "../hooks/useNotification";
|
||||
import { ThemeToggle } from "../components/common/ThemeToggle";
|
||||
import { Icon } from "@iconify/react";
|
||||
import { dbManager } from "../utils/indexedDB";
|
||||
|
||||
const LoginPage: React.FC = () => {
|
||||
const [username, setUsername] = useState("");
|
||||
@ -15,6 +16,7 @@ const LoginPage: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { showNotification } = useNotification();
|
||||
const isAuthenticated = useAppSelector((state) => state.auth.isAuthenticated);
|
||||
const currentUserId = useAppSelector((state) => state.auth.userId);
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
useEffect(() => {
|
||||
@ -48,9 +50,17 @@ const LoginPage: React.FC = () => {
|
||||
if (data.success) {
|
||||
// Получаем информацию о пользователе
|
||||
const authStatus = await authApi.checkStatus();
|
||||
const newUserId = authStatus.userId!;
|
||||
|
||||
// Если пользователь изменился, очищаем IndexedDB
|
||||
if (currentUserId && currentUserId !== newUserId) {
|
||||
console.log(`[Login] User changed from ${currentUserId} to ${newUserId}, clearing IndexedDB`);
|
||||
await dbManager.clearAll();
|
||||
}
|
||||
|
||||
dispatch(
|
||||
setAuth({
|
||||
userId: authStatus.userId!,
|
||||
userId: newUserId,
|
||||
username: authStatus.username!,
|
||||
})
|
||||
);
|
||||
|
||||
@ -11,6 +11,7 @@ import { setAccentColor } from "../utils/colorUtils";
|
||||
import { useNotification } from "../hooks/useNotification";
|
||||
import { Modal } from "../components/common/Modal";
|
||||
import { ThemeToggle } from "../components/common/ThemeToggle";
|
||||
import { dbManager } from "../utils/indexedDB";
|
||||
|
||||
const ProfilePage: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
@ -211,6 +212,8 @@ const ProfilePage: React.FC = () => {
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await userApi.deleteAccount(deletePassword);
|
||||
// Очищаем IndexedDB при удалении аккаунта
|
||||
await dbManager.clearAll();
|
||||
showNotification("Аккаунт успешно удален", "success");
|
||||
dispatch(clearAuth());
|
||||
setTimeout(() => {
|
||||
@ -259,10 +262,13 @@ const ProfilePage: React.FC = () => {
|
||||
onClick={async () => {
|
||||
try {
|
||||
await authApi.logout();
|
||||
dispatch(clearAuth());
|
||||
navigate("/");
|
||||
} catch (error) {
|
||||
console.error("Ошибка выхода:", error);
|
||||
} finally {
|
||||
// Очищаем IndexedDB в фоне (не блокируем выход)
|
||||
dbManager.clearAll().catch((err) => {
|
||||
console.error("Ошибка очистки IndexedDB при выходе:", err);
|
||||
});
|
||||
dispatch(clearAuth());
|
||||
navigate("/");
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import { authApi } from "../api/authApi";
|
||||
import { useNotification } from "../hooks/useNotification";
|
||||
import { ThemeToggle } from "../components/common/ThemeToggle";
|
||||
import { Icon } from "@iconify/react";
|
||||
import { dbManager } from "../utils/indexedDB";
|
||||
|
||||
const RegisterPage: React.FC = () => {
|
||||
const [username, setUsername] = useState("");
|
||||
@ -57,9 +58,15 @@ const RegisterPage: React.FC = () => {
|
||||
if (data.success) {
|
||||
// Получаем информацию о пользователе
|
||||
const authStatus = await authApi.checkStatus();
|
||||
const newUserId = authStatus.userId!;
|
||||
|
||||
// При регистрации нового пользователя всегда очищаем IndexedDB
|
||||
// (на случай, если в базе остались данные предыдущего пользователя)
|
||||
await dbManager.clearAll();
|
||||
|
||||
dispatch(
|
||||
setAuth({
|
||||
userId: authStatus.userId!,
|
||||
userId: newUserId,
|
||||
username: authStatus.username!,
|
||||
})
|
||||
);
|
||||
|
||||
@ -553,10 +553,13 @@ const SettingsPage: React.FC = () => {
|
||||
onClick={async () => {
|
||||
try {
|
||||
await authApi.logout();
|
||||
dispatch(clearAuth());
|
||||
navigate("/");
|
||||
} catch (error) {
|
||||
console.error("Ошибка выхода:", error);
|
||||
} finally {
|
||||
// Очищаем IndexedDB в фоне (не блокируем выход)
|
||||
dbManager.clearAll().catch((err) => {
|
||||
console.error("Ошибка очистки IndexedDB при выходе:", err);
|
||||
});
|
||||
dispatch(clearAuth());
|
||||
navigate("/");
|
||||
}
|
||||
|
||||
@ -225,6 +225,16 @@ class IndexedDBManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Полная очистка всех данных из IndexedDB (заметки и очередь синхронизации)
|
||||
*/
|
||||
async clearAll(): Promise<void> {
|
||||
await Promise.all([
|
||||
this.clearAllNotes(),
|
||||
this.clearSyncQueue(),
|
||||
]);
|
||||
}
|
||||
|
||||
// ===== Утилиты =====
|
||||
|
||||
async getPendingSyncCount(): Promise<number> {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user