215 lines
6.3 KiB
JavaScript
215 lines
6.3 KiB
JavaScript
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(`📝 Приложение для заметок готово к работе!`);
|
||
});
|