✨ Упрощено логирование действий пользователя и удалены функции получения IP-адреса
- Удалена функция получения IP-адреса клиента, так как она больше не используется. - Обновлена функция логирования действий пользователя для исключения параметра IP-адреса. - Обновлены все вызовы логирования в коде, чтобы соответствовать новым изменениям. - Удалены ссылки на IP-адрес в интерфейсе отображения логов действий.
This commit is contained in:
parent
84291aac91
commit
5306122a7d
@ -302,7 +302,6 @@
|
|||||||
<th>Дата и время</th>
|
<th>Дата и время</th>
|
||||||
<th>Действие</th>
|
<th>Действие</th>
|
||||||
<th>Детали</th>
|
<th>Детали</th>
|
||||||
<th>IP-адрес</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="logsTableBody">
|
<tbody id="logsTableBody">
|
||||||
|
|||||||
@ -823,7 +823,6 @@ async function loadLogs(reset = false) {
|
|||||||
log.action_type
|
log.action_type
|
||||||
}">${actionText}</span></td>
|
}">${actionText}</span></td>
|
||||||
<td>${log.details || "-"}</td>
|
<td>${log.details || "-"}</td>
|
||||||
<td>${log.ip_address || "-"}</td>
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
tbody.appendChild(row);
|
tbody.appendChild(row);
|
||||||
|
|||||||
202
server.js
202
server.js
@ -360,114 +360,18 @@ function createIndexes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Функция для логирования действий пользователя
|
// Функция для логирования действий пользователя
|
||||||
function logAction(userId, actionType, details, ipAddress) {
|
function logAction(userId, actionType, details) {
|
||||||
const sql = `
|
const sql = `
|
||||||
INSERT INTO action_logs (user_id, action_type, details, ip_address)
|
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) {
|
if (err) {
|
||||||
console.error("Ошибка логирования действия:", err.message);
|
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() {
|
function runMigrations() {
|
||||||
// Создаем таблицу логов действий, если её нет
|
// Создаем таблицу логов действий, если её нет
|
||||||
@ -744,13 +648,7 @@ app.post("/api/register", async (req, res) => {
|
|||||||
req.session.authenticated = true;
|
req.session.authenticated = true;
|
||||||
|
|
||||||
// Логируем регистрацию
|
// Логируем регистрацию
|
||||||
const clientIP = getClientIP(req);
|
logAction(this.lastID, "register", `Регистрация нового пользователя`);
|
||||||
logAction(
|
|
||||||
this.lastID,
|
|
||||||
"register",
|
|
||||||
`Регистрация нового пользователя`,
|
|
||||||
clientIP
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({ success: true, message: "Регистрация успешна" });
|
res.json({ success: true, message: "Регистрация успешна" });
|
||||||
});
|
});
|
||||||
@ -792,8 +690,7 @@ app.post("/api/login", async (req, res) => {
|
|||||||
req.session.authenticated = true;
|
req.session.authenticated = true;
|
||||||
|
|
||||||
// Логируем вход
|
// Логируем вход
|
||||||
const clientIP = getClientIP(req);
|
logAction(user.id, "login", `Вход в систему`);
|
||||||
logAction(user.id, "login", `Вход в систему`, clientIP);
|
|
||||||
|
|
||||||
res.json({ success: true, message: "Вход успешен" });
|
res.json({ success: true, message: "Вход успешен" });
|
||||||
} catch (err) {
|
} 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) => {
|
app.get("/notes", requireAuth, (req, res) => {
|
||||||
// Получаем цвет пользователя для предотвращения FOUC
|
// Получаем цвет пользователя для предотвращения FOUC
|
||||||
@ -1070,14 +944,8 @@ app.post("/api/notes", requireApiAuth, (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Логируем создание заметки
|
// Логируем создание заметки
|
||||||
const clientIP = getClientIP(req);
|
|
||||||
const noteId = this.lastID;
|
const noteId = this.lastID;
|
||||||
logAction(
|
logAction(req.session.userId, "note_create", `Создана заметка #${noteId}`);
|
||||||
req.session.userId,
|
|
||||||
"note_create",
|
|
||||||
`Создана заметка #${noteId}`,
|
|
||||||
clientIP
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({ id: noteId, content, date, time });
|
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}`);
|
||||||
logAction(
|
|
||||||
req.session.userId,
|
|
||||||
"note_update",
|
|
||||||
`Обновлена заметка #${id}`,
|
|
||||||
clientIP
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({ id, content, date: row.date, time: row.time });
|
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(
|
logAction(
|
||||||
req.session.userId,
|
req.session.userId,
|
||||||
"note_delete",
|
"note_delete",
|
||||||
`Удалена заметка #${id}`,
|
`Удалена заметка #${id}`
|
||||||
clientIP
|
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({ message: "Заметка удалена" });
|
res.json({ message: "Заметка удалена" });
|
||||||
@ -1730,14 +1590,13 @@ app.put("/api/user/profile", requireApiAuth, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Логируем обновление профиля
|
// Логируем обновление профиля
|
||||||
const clientIP = getClientIP(req);
|
|
||||||
const changes = [];
|
const changes = [];
|
||||||
if (username && username !== user.username) changes.push("логин");
|
if (username && username !== user.username) changes.push("логин");
|
||||||
if (email !== undefined) changes.push("email");
|
if (email !== undefined) changes.push("email");
|
||||||
if (accent_color !== undefined) changes.push("цвет темы");
|
if (accent_color !== undefined) changes.push("цвет темы");
|
||||||
if (newPassword) changes.push("пароль");
|
if (newPassword) changes.push("пароль");
|
||||||
const details = `Обновлен профиль: ${changes.join(", ")}`;
|
const details = `Обновлен профиль: ${changes.join(", ")}`;
|
||||||
logAction(userId, "profile_update", details, clientIP);
|
logAction(userId, "profile_update", details);
|
||||||
|
|
||||||
res.json({ success: true, message: "Профиль успешно обновлен" });
|
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 ? "закреплена" : "откреплена";
|
const action = newPinState ? "закреплена" : "откреплена";
|
||||||
logAction(
|
logAction(req.session.userId, "note_pin", `Заметка #${id} ${action}`);
|
||||||
req.session.userId,
|
|
||||||
"note_pin",
|
|
||||||
`Заметка #${id} ${action}`,
|
|
||||||
clientIP
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({ success: true, is_pinned: newPinState });
|
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(
|
logAction(
|
||||||
req.session.userId,
|
req.session.userId,
|
||||||
"note_archive",
|
"note_archive",
|
||||||
`Заметка #${id} архивирована`,
|
`Заметка #${id} архивирована`
|
||||||
clientIP
|
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({ success: true, message: "Заметка архивирована" });
|
res.json({ success: true, message: "Заметка архивирована" });
|
||||||
@ -1943,12 +1794,10 @@ app.put("/api/notes/:id/unarchive", requireApiAuth, (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Логируем действие
|
// Логируем действие
|
||||||
const clientIP = getClientIP(req);
|
|
||||||
logAction(
|
logAction(
|
||||||
req.session.userId,
|
req.session.userId,
|
||||||
"note_unarchive",
|
"note_unarchive",
|
||||||
`Заметка #${id} восстановлена из архива`,
|
`Заметка #${id} восстановлена из архива`
|
||||||
clientIP
|
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({ success: true, message: "Заметка восстановлена" });
|
res.json({ success: true, message: "Заметка восстановлена" });
|
||||||
@ -2125,12 +1974,10 @@ app.delete("/api/notes/archived/all", requireApiAuth, async (req, res) => {
|
|||||||
const deletedCount = this.changes;
|
const deletedCount = this.changes;
|
||||||
|
|
||||||
// Логируем действие
|
// Логируем действие
|
||||||
const clientIP = getClientIP(req);
|
|
||||||
logAction(
|
logAction(
|
||||||
req.session.userId,
|
req.session.userId,
|
||||||
"note_delete_permanent",
|
"note_delete_permanent",
|
||||||
`Удалены все архивные заметки (${deletedCount} шт.)`,
|
`Удалены все архивные заметки (${deletedCount} шт.)`
|
||||||
clientIP
|
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
@ -2228,12 +2075,10 @@ app.delete("/api/notes/archived/:id", requireApiAuth, (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Логируем действие
|
// Логируем действие
|
||||||
const clientIP = getClientIP(req);
|
|
||||||
logAction(
|
logAction(
|
||||||
req.session.userId,
|
req.session.userId,
|
||||||
"note_delete_permanent",
|
"note_delete_permanent",
|
||||||
`Заметка #${id} окончательно удалена из архива`,
|
`Заметка #${id} окончательно удалена из архива`
|
||||||
clientIP
|
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({ success: true, message: "Заметка удалена окончательно" });
|
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;
|
const { action_type, limit = 100, offset = 0 } = req.query;
|
||||||
|
|
||||||
let sql = `
|
let sql = `
|
||||||
SELECT id, action_type, details, ip_address, created_at
|
SELECT id, action_type, details, created_at
|
||||||
FROM action_logs
|
FROM action_logs
|
||||||
WHERE user_id = ?
|
WHERE user_id = ?
|
||||||
`;
|
`;
|
||||||
@ -2359,8 +2204,7 @@ app.put("/api/user/ai-settings", requireApiAuth, (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Логируем обновление AI настроек
|
// Логируем обновление AI настроек
|
||||||
const clientIP = getClientIP(req);
|
logAction(userId, "profile_update", "Изменен статус AI");
|
||||||
logAction(userId, "profile_update", "Изменен статус AI", clientIP);
|
|
||||||
|
|
||||||
res.json({ success: true, message: "Настройки AI успешно сохранены" });
|
res.json({ success: true, message: "Настройки AI успешно сохранены" });
|
||||||
});
|
});
|
||||||
@ -2401,8 +2245,7 @@ app.put("/api/user/ai-settings", requireApiAuth, (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Логируем обновление AI настроек
|
// Логируем обновление AI настроек
|
||||||
const clientIP = getClientIP(req);
|
logAction(userId, "profile_update", "Обновлены AI настройки");
|
||||||
logAction(userId, "profile_update", "Обновлены AI настройки", clientIP);
|
|
||||||
|
|
||||||
res.json({ success: true, message: "AI настройки успешно сохранены" });
|
res.json({ success: true, message: "AI настройки успешно сохранены" });
|
||||||
});
|
});
|
||||||
@ -2527,13 +2370,7 @@ app.post("/api/ai/improve", requireApiAuth, async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Логируем использование AI
|
// Логируем использование AI
|
||||||
const clientIP = getClientIP(req);
|
logAction(req.session.userId, "ai_improve", "Улучшен текст через AI");
|
||||||
logAction(
|
|
||||||
req.session.userId,
|
|
||||||
"ai_improve",
|
|
||||||
"Улучшен текст через AI",
|
|
||||||
clientIP
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({ success: true, improvedText });
|
res.json({ success: true, improvedText });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -2552,11 +2389,10 @@ app.post("/api/ai/improve", requireApiAuth, async (req, res) => {
|
|||||||
// Выход
|
// Выход
|
||||||
app.post("/logout", (req, res) => {
|
app.post("/logout", (req, res) => {
|
||||||
const userId = req.session.userId;
|
const userId = req.session.userId;
|
||||||
const clientIP = getClientIP(req);
|
|
||||||
|
|
||||||
// Логируем выход
|
// Логируем выход
|
||||||
if (userId) {
|
if (userId) {
|
||||||
logAction(userId, "logout", "Выход из системы", clientIP);
|
logAction(userId, "logout", "Выход из системы");
|
||||||
}
|
}
|
||||||
|
|
||||||
req.session.destroy((err) => {
|
req.session.destroy((err) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user