NoteJS/server.js

215 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require("express");
const sqlite3 = require("sqlite3").verbose();
const bcrypt = require("bcryptjs");
const session = require("express-session");
const path = require("path");
const helmet = require("helmet");
const rateLimit = require("express-rate-limit");
const bodyParser = require("body-parser");
require("dotenv").config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware для безопасности
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
"'unsafe-inline'",
"https://cdnjs.cloudflare.com",
],
styleSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"],
fontSrc: ["'self'", "https://cdnjs.cloudflare.com", "data:"],
imgSrc: ["'self'", "data:"],
},
},
})
);
// Ограничение запросов
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 минут
max: 100, // максимум 100 запросов с одного IP
});
app.use(limiter);
// Статические файлы
app.use(express.static(path.join(__dirname, "public")));
// Парсинг тела запроса
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// Настройка сессий
app.use(
session({
secret: process.env.SESSION_SECRET || "default-secret",
resave: false,
saveUninitialized: false,
cookie: { secure: false }, // в продакшене установить true с HTTPS
})
);
// Подключение к базе данных
const db = new sqlite3.Database("./notes.db", (err) => {
if (err) {
console.error("Ошибка подключения к базе данных:", err.message);
} else {
console.log("Подключено к SQLite базе данных");
createTables();
}
});
// Создание таблиц
function createTables() {
const createNotesTable = `
CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
date TEXT NOT NULL,
time TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`;
db.run(createNotesTable, (err) => {
if (err) {
console.error("Ошибка создания таблицы заметок:", err.message);
} else {
console.log("Таблица заметок готова");
}
});
}
// Middleware для аутентификации
function requireAuth(req, res, next) {
if (req.session.authenticated) {
return next();
} else {
return res.redirect("/");
}
}
// Маршруты
// Главная страница с формой входа
app.get("/", (req, res) => {
if (req.session.authenticated) {
return res.redirect("/notes");
}
res.sendFile(path.join(__dirname, "public", "index.html"));
});
// Обработка входа
app.post("/login", async (req, res) => {
const { password } = req.body;
const correctPassword = process.env.APP_PASSWORD;
if (password === correctPassword) {
req.session.authenticated = true;
res.redirect("/notes");
} else {
res.redirect("/?error=invalid_password");
}
});
// Страница с заметками (требует аутентификации)
app.get("/notes", requireAuth, (req, res) => {
res.sendFile(path.join(__dirname, "public", "notes.html"));
});
// API для получения всех заметок
app.get("/api/notes", requireAuth, (req, res) => {
const sql = "SELECT * FROM notes ORDER BY created_at ASC";
db.all(sql, [], (err, rows) => {
if (err) {
console.error("Ошибка получения заметок:", err.message);
return res.status(500).json({ error: "Ошибка сервера" });
}
res.json(rows);
});
});
// API для создания новой заметки
app.post("/api/notes", requireAuth, (req, res) => {
const { content, date, time } = req.body;
if (!content || !date || !time) {
return res.status(400).json({ error: "Не все поля заполнены" });
}
const sql = "INSERT INTO notes (content, date, time) VALUES (?, ?, ?)";
const params = [content, date, time];
db.run(sql, params, function (err) {
if (err) {
console.error("Ошибка создания заметки:", err.message);
return res.status(500).json({ error: "Ошибка сервера" });
}
res.json({ id: this.lastID, content, date, time });
});
});
// API для обновления заметки
app.put("/api/notes/:id", requireAuth, (req, res) => {
const { content, date, time } = req.body;
const { id } = req.params;
if (!content || !date || !time) {
return res.status(400).json({ error: "Не все поля заполнены" });
}
const sql = "UPDATE notes SET content = ?, date = ?, time = ? WHERE id = ?";
const params = [content, date, time, id];
db.run(sql, params, function (err) {
if (err) {
console.error("Ошибка обновления заметки:", err.message);
return res.status(500).json({ error: "Ошибка сервера" });
}
if (this.changes === 0) {
return res.status(404).json({ error: "Заметка не найдена" });
}
res.json({ id, content, date, time });
});
});
// API для удаления заметки
app.delete("/api/notes/:id", requireAuth, (req, res) => {
const { id } = req.params;
const sql = "DELETE FROM notes WHERE id = ?";
db.run(sql, id, function (err) {
if (err) {
console.error("Ошибка удаления заметки:", err.message);
return res.status(500).json({ error: "Ошибка сервера" });
}
if (this.changes === 0) {
return res.status(404).json({ error: "Заметка не найдена" });
}
res.json({ message: "Заметка удалена" });
});
});
// Выход
app.post("/logout", (req, res) => {
req.session.destroy((err) => {
if (err) {
return res.status(500).json({ error: "Ошибка выхода" });
}
res.redirect("/");
});
});
// Запуск сервера
app.listen(PORT, () => {
console.log(`🚀 Сервер запущен на порту ${PORT}`);
console.log(`📝 Приложение для заметок готово к работе!`);
});