diff --git a/public/settings.html b/public/settings.html index 6e161fa..2432110 100644 --- a/public/settings.html +++ b/public/settings.html @@ -302,7 +302,6 @@ Дата и время Действие Детали - IP-адрес diff --git a/public/settings.js b/public/settings.js index 0c1541a..ae36571 100644 --- a/public/settings.js +++ b/public/settings.js @@ -823,7 +823,6 @@ async function loadLogs(reset = false) { log.action_type }">${actionText} ${log.details || "-"} - ${log.ip_address || "-"} `; tbody.appendChild(row); diff --git a/server.js b/server.js index c52d242..69b6cf1 100644 --- a/server.js +++ b/server.js @@ -360,114 +360,18 @@ function createIndexes() { } // Функция для логирования действий пользователя -function logAction(userId, actionType, details, ipAddress) { +function logAction(userId, actionType, details) { const sql = ` INSERT INTO action_logs (user_id, action_type, details, ip_address) - VALUES (?, ?, ?, ?) + VALUES (?, ?, ?, NULL) `; - db.run(sql, [userId, actionType, details, ipAddress], (err) => { + db.run(sql, [userId, actionType, details], (err) => { if (err) { console.error("Ошибка логирования действия:", err.message); } }); } -// Функция для получения IP-адреса клиента -function getClientIP(req) { - // Логируем все заголовки для отладки - console.log("=== IP Headers Debug ==="); - console.log("req.ip:", req.ip); - console.log("x-forwarded-for:", req.headers["x-forwarded-for"]); - console.log("x-real-ip:", req.headers["x-real-ip"]); - console.log("x-client-ip:", req.headers["x-client-ip"]); - console.log("cf-connecting-ip:", req.headers["cf-connecting-ip"]); // Cloudflare - console.log("x-forwarded:", req.headers["x-forwarded"]); - console.log("forwarded:", req.headers["forwarded"]); - console.log("req.connection.remoteAddress:", req.connection?.remoteAddress); - console.log("req.socket.remoteAddress:", req.socket?.remoteAddress); - console.log("ALL HEADERS:", JSON.stringify(req.headers, null, 2)); - console.log("========================"); - - // Приоритет для получения реального IP клиента: - // 1. req.socket.remoteAddress (для случаев с дополнительными прокси) - // 2. X-Real-IP (часто используется nginx) - // 3. X-Forwarded-For (стандартный заголовок) - // 4. CF-Connecting-IP (если используется Cloudflare) - // 5. X-Client-IP - // 6. req.ip (Express с trust proxy) - - let ip = null; - - // Проверяем socket_remoteAddress в первую очередь (для случаев с дополнительными прокси) - if ( - req.socket?.remoteAddress && - req.socket.remoteAddress !== "::1" && - req.socket.remoteAddress !== "127.0.0.1" - ) { - ip = req.socket.remoteAddress; - } - // Проверяем X-Real-IP (nginx часто использует этот заголовок) - else if (req.headers["x-real-ip"]) { - ip = req.headers["x-real-ip"]; - } - // Проверяем X-Forwarded-For (берем первый IP из списка) - else if (req.headers["x-forwarded-for"]) { - ip = req.headers["x-forwarded-for"].split(",")[0].trim(); - } - // Проверяем CF-Connecting-IP (Cloudflare) - else if (req.headers["cf-connecting-ip"]) { - ip = req.headers["cf-connecting-ip"]; - } - // Проверяем X-Client-IP - else if (req.headers["x-client-ip"]) { - ip = req.headers["x-client-ip"]; - } - // Проверяем Forwarded заголовок (RFC 7239) - else if (req.headers["forwarded"]) { - const forwarded = req.headers["forwarded"]; - const forMatch = forwarded.match(/for=([^;,\s]+)/); - if (forMatch) { - ip = forMatch[1]; - } - } - // Используем req.ip (Express с trust proxy) - else if (req.ip && req.ip !== "::1" && req.ip !== "127.0.0.1") { - ip = req.ip; - } - - // Очищаем IP от скобок IPv6, портов и кавычек - if (ip && ip !== "unknown") { - // Убираем скобки IPv6 - ip = ip.replace(/[[\]]/g, ""); - // Убираем кавычки если есть - ip = ip.replace(/['"]/g, ""); - - // Обрабатываем IPv6 адреса - if (ip.startsWith("::ffff:")) { - // IPv4-mapped IPv6 address (::ffff:192.168.1.1) - ip = ip.substring(7); // Убираем "::ffff:" - } else if (ip.includes("::")) { - const ipv6Match = ip.match(/^(\[)?([^\]]+)(\])?(:(\d+))?$/); - if (ipv6Match) { - ip = ipv6Match[2]; - } - } else if (ip.includes(":")) { - // IPv4 с портом - const parts = ip.split(":"); - if (parts.length === 2 && /^\d+$/.test(parts[1])) { - ip = parts[0]; - } - } - } - - // Конвертируем IPv6 localhost в IPv4 - if (ip === "::1" || ip === "::ffff:127.0.0.1") { - ip = "127.0.0.1"; - } - - return ip || "unknown"; -} - // Миграции базы данных function runMigrations() { // Создаем таблицу логов действий, если её нет @@ -744,13 +648,7 @@ app.post("/api/register", async (req, res) => { req.session.authenticated = true; // Логируем регистрацию - const clientIP = getClientIP(req); - logAction( - this.lastID, - "register", - `Регистрация нового пользователя`, - clientIP - ); + logAction(this.lastID, "register", `Регистрация нового пользователя`); res.json({ success: true, message: "Регистрация успешна" }); }); @@ -792,8 +690,7 @@ app.post("/api/login", async (req, res) => { req.session.authenticated = true; // Логируем вход - const clientIP = getClientIP(req); - logAction(user.id, "login", `Вход в систему`, clientIP); + logAction(user.id, "login", `Вход в систему`); res.json({ success: true, message: "Вход успешен" }); } catch (err) { @@ -851,29 +748,6 @@ app.get("/api/user", requireApiAuth, (req, res) => { }); }); -// Тестовый эндпоинт для проверки IP-адресов (только для отладки) -app.get("/api/debug/ip", (req, res) => { - const clientIP = getClientIP(req); - - res.json({ - detected_ip: clientIP, - headers: { - "x-forwarded-for": req.headers["x-forwarded-for"], - "x-real-ip": req.headers["x-real-ip"], - "x-client-ip": req.headers["x-client-ip"], - "cf-connecting-ip": req.headers["cf-connecting-ip"], - "x-forwarded": req.headers["x-forwarded"], - forwarded: req.headers["forwarded"], - }, - express: { - req_ip: req.ip, - socket_remoteAddress: req.socket?.remoteAddress, - connection_remoteAddress: req.connection?.remoteAddress, - }, - all_headers: req.headers, - }); -}); - // Страница с заметками (требует аутентификации) app.get("/notes", requireAuth, (req, res) => { // Получаем цвет пользователя для предотвращения FOUC @@ -1070,14 +944,8 @@ app.post("/api/notes", requireApiAuth, (req, res) => { } // Логируем создание заметки - const clientIP = getClientIP(req); const noteId = this.lastID; - logAction( - req.session.userId, - "note_create", - `Создана заметка #${noteId}`, - clientIP - ); + logAction(req.session.userId, "note_create", `Создана заметка #${noteId}`); res.json({ id: noteId, content, date, time }); }); @@ -1122,13 +990,7 @@ app.put("/api/notes/:id", requireApiAuth, (req, res) => { } // Логируем обновление заметки - const clientIP = getClientIP(req); - logAction( - req.session.userId, - "note_update", - `Обновлена заметка #${id}`, - clientIP - ); + logAction(req.session.userId, "note_update", `Обновлена заметка #${id}`); res.json({ id, content, date: row.date, time: row.time }); }); @@ -1213,12 +1075,10 @@ app.delete("/api/notes/:id", requireApiAuth, (req, res) => { } // Логируем удаление заметки - const clientIP = getClientIP(req); logAction( req.session.userId, "note_delete", - `Удалена заметка #${id}`, - clientIP + `Удалена заметка #${id}` ); res.json({ message: "Заметка удалена" }); @@ -1730,14 +1590,13 @@ app.put("/api/user/profile", requireApiAuth, async (req, res) => { } // Логируем обновление профиля - const clientIP = getClientIP(req); const changes = []; if (username && username !== user.username) changes.push("логин"); if (email !== undefined) changes.push("email"); if (accent_color !== undefined) changes.push("цвет темы"); if (newPassword) changes.push("пароль"); const details = `Обновлен профиль: ${changes.join(", ")}`; - logAction(userId, "profile_update", details, clientIP); + logAction(userId, "profile_update", details); res.json({ success: true, message: "Профиль успешно обновлен" }); }); @@ -1857,14 +1716,8 @@ app.put("/api/notes/:id/pin", requireApiAuth, (req, res) => { } // Логируем действие - const clientIP = getClientIP(req); const action = newPinState ? "закреплена" : "откреплена"; - logAction( - req.session.userId, - "note_pin", - `Заметка #${id} ${action}`, - clientIP - ); + logAction(req.session.userId, "note_pin", `Заметка #${id} ${action}`); res.json({ success: true, is_pinned: newPinState }); }); @@ -1901,12 +1754,10 @@ app.put("/api/notes/:id/archive", requireApiAuth, (req, res) => { } // Логируем действие - const clientIP = getClientIP(req); logAction( req.session.userId, "note_archive", - `Заметка #${id} архивирована`, - clientIP + `Заметка #${id} архивирована` ); res.json({ success: true, message: "Заметка архивирована" }); @@ -1943,12 +1794,10 @@ app.put("/api/notes/:id/unarchive", requireApiAuth, (req, res) => { } // Логируем действие - const clientIP = getClientIP(req); logAction( req.session.userId, "note_unarchive", - `Заметка #${id} восстановлена из архива`, - clientIP + `Заметка #${id} восстановлена из архива` ); res.json({ success: true, message: "Заметка восстановлена" }); @@ -2125,12 +1974,10 @@ app.delete("/api/notes/archived/all", requireApiAuth, async (req, res) => { const deletedCount = this.changes; // Логируем действие - const clientIP = getClientIP(req); logAction( req.session.userId, "note_delete_permanent", - `Удалены все архивные заметки (${deletedCount} шт.)`, - clientIP + `Удалены все архивные заметки (${deletedCount} шт.)` ); res.json({ @@ -2228,12 +2075,10 @@ app.delete("/api/notes/archived/:id", requireApiAuth, (req, res) => { } // Логируем действие - const clientIP = getClientIP(req); logAction( req.session.userId, "note_delete_permanent", - `Заметка #${id} окончательно удалена из архива`, - clientIP + `Заметка #${id} окончательно удалена из архива` ); res.json({ success: true, message: "Заметка удалена окончательно" }); @@ -2248,7 +2093,7 @@ app.get("/api/logs", requireApiAuth, (req, res) => { const { action_type, limit = 100, offset = 0 } = req.query; let sql = ` - SELECT id, action_type, details, ip_address, created_at + SELECT id, action_type, details, created_at FROM action_logs WHERE user_id = ? `; @@ -2359,8 +2204,7 @@ app.put("/api/user/ai-settings", requireApiAuth, (req, res) => { } // Логируем обновление AI настроек - const clientIP = getClientIP(req); - logAction(userId, "profile_update", "Изменен статус AI", clientIP); + logAction(userId, "profile_update", "Изменен статус AI"); res.json({ success: true, message: "Настройки AI успешно сохранены" }); }); @@ -2401,8 +2245,7 @@ app.put("/api/user/ai-settings", requireApiAuth, (req, res) => { } // Логируем обновление AI настроек - const clientIP = getClientIP(req); - logAction(userId, "profile_update", "Обновлены AI настройки", clientIP); + logAction(userId, "profile_update", "Обновлены AI настройки"); res.json({ success: true, message: "AI настройки успешно сохранены" }); }); @@ -2527,13 +2370,7 @@ app.post("/api/ai/improve", requireApiAuth, async (req, res) => { }); // Логируем использование AI - const clientIP = getClientIP(req); - logAction( - req.session.userId, - "ai_improve", - "Улучшен текст через AI", - clientIP - ); + logAction(req.session.userId, "ai_improve", "Улучшен текст через AI"); res.json({ success: true, improvedText }); } catch (error) { @@ -2552,11 +2389,10 @@ app.post("/api/ai/improve", requireApiAuth, async (req, res) => { // Выход app.post("/logout", (req, res) => { const userId = req.session.userId; - const clientIP = getClientIP(req); // Логируем выход if (userId) { - logAction(userId, "logout", "Выход из системы", clientIP); + logAction(userId, "logout", "Выход из системы"); } req.session.destroy((err) => {