feat: frontend, sqlite integration

This commit is contained in:
2025-12-26 21:05:11 +03:00
parent 869bc60b60
commit ca4d4cd685
8 changed files with 1254 additions and 224 deletions

1
backend/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
notes.db

View File

@@ -11,6 +11,7 @@
"dependencies": {
"cors": "^2.8.5",
"express": "^5.2.1",
"sqlite": "^5.1.1",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1"
}
@@ -1017,6 +1018,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sqlite": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz",
"integrity": "sha512-oBkezXa2hnkfuJwUo44Hl9hS3er+YFtueifoajrgidvqsJRQFpc5fKoAkAor1O5ZnLoa28GBScfHXs8j0K358Q==",
"license": "MIT"
},
"node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",

View File

@@ -13,6 +13,7 @@
"dependencies": {
"cors": "^2.8.5",
"express": "^5.2.1",
"sqlite": "^5.1.1",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1"
}

View File

@@ -1,256 +1,182 @@
import express from 'express';
import { json } from 'express';
import cors from 'cors';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Создаем экземпляр приложения Express
const app = express();
// Подключаем наши middleware (промежуточные обработчики маршрутов)
// middlware CORS разрешает запросы с других доменов. Без этого браузер
// запретит фронтенду общаться с бекендом!
app.use(cors({
origin: 'http://localhost:8080', // разрешаем только с этого домена
methods: ['GET', 'POST', 'PUT', 'DELETE'], // разрешаем только эти методы
allowedHeaders: ['Content-Type'] // разрешаем только эти заголовки
origin: 'http://localhost:8080',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type']
}));
// JSON парсер позволяет обрабатывать JSON в теле запросов
app.use(json());
// Создаем массив для хранения заметок в памяти
// В реальном приложении здесь была бы база данных
let notes = [];
let db;
// Переменная для генерации уникальных ID
// Каждая новая заметка получит ID на 1 больше предыдущей
let noteIdCounter = 1;
// ==================== РАЗДЕЛ: РОУТЫ API (маршруты) ====================
// РОУТ 1: Получение всех заметок (READ)
// Метод: GET
// Адрес: /notes
app.get('/notes', function(request, response) {
/*
Эта функция обрабатывает запрос на получение всех заметок
request - объект запроса (содержит данные от клиента)
response - объект ответа (используем для отправки данных клиенту)
*/
console.log('Получен запрос на получение всех заметок');
// Отправляем клиенту все заметки в формате JSON
// status(200) - код 200 означает "Успешно"
response.status(200).json({
success: true, // флаг успешного выполнения
data: notes, // сами заметки
count: notes.length // количество заметок
async function initializeDatabase() {
db = await open({
filename: './notes.db',
driver: sqlite3.Database
});
});
// РОУТ 2: Получение одной заметки по ID (READ)
// Метод: GET
// Адрес: /notes/:id
// :id - это параметр маршрута (динамическая часть URL)
app.get('/notes/:id', function(request, response) {
/*
Эта функция ищет заметку по ID
request.params.id - получаем ID из URL
*/
console.log('Получен запрос на получение заметки с ID:', request.params.id);
// Преобразуем ID из строки в число
const noteId = parseInt(request.params.id);
// Ищем заметку в массиве по ID.
// В качестве аргумента notes.find() используем
// функцию function(note) {}, которая определяет,
// совпадает ли id некоторой заметки с id нашей
const foundNote = notes.find(function(note) {
return note.id === noteId;
});
// Если заметка не найдена
if (!foundNote) {
console.log('Заметка не найдена');
return response.status(404).json({
await db.exec(`
CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
text TEXT NOT NULL,
author TEXT NOT NULL,
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
}
initializeDatabase();
app.get('/notes', async (req, res) => {
try {
const notes = await db.all('SELECT * FROM notes ORDER BY createdAt DESC');
res.status(200).json({
success: true,
data: notes,
count: notes.length
});
} catch (error) {
res.status(500).json({
success: false,
message: 'Заметка с таким ID не найдена'
message: 'Ошибка при получении заметок'
});
}
// Если заметка найдена - отправляем ее клиенту
response.status(200).json({
success: true,
data: foundNote
});
});
// РОУТ 3: Создание новой заметки (CREATE)
// Метод: POST
// Адрес: /notes
app.post('/notes', function(request, response) {
/*
Эта функция создает новую заметку
request.body - содержит данные, отправленные клиентом
*/
console.log('Получен запрос на создание заметки');
app.get('/notes/:id', async (req, res) => {
try {
const note = await db.get('SELECT * FROM notes WHERE id = ?', req.params.id);
if (!note) {
return res.status(404).json({
success: false,
message: 'Заметка с таким ID не найдена'
});
}
res.status(200).json({
success: true,
data: note
});
} catch (error) {
res.status(500).json({
success: false,
message: 'Ошибка при получении заметки'
});
}
});
app.post('/notes', async (req, res) => {
const { title, text, author } = req.body;
// Получаем данные из тела запроса
const title = request.body.title;
const text = request.body.text;
const author = request.body.author;
// Проверяем, все ли обязательные поля заполнены
if (!title || !text || !author) {
console.log('Ошибка: не все поля заполнены');
return response.status(400).json({
return res.status(400).json({
success: false,
message: 'Пожалуйста, заполните все поля: title, text, author'
});
}
// Создаем новую заметку
const newNote = {
id: noteIdCounter, // присваиваем уникальный ID
title: title, // заголовок из запроса
text: text, // текст заметки из запроса
author: author, // автор из запроса
createdAt: new Date(), // дата создания (текущее время)
updatedAt: new Date() // дата обновления (пока равна дате создания)
};
// Добавляем заметку в массив
notes.push(newNote);
// Увеличиваем счетчик ID для следующей заметки
noteIdCounter++;
console.log('Создана новая заметка с ID:', newNote.id);
// Отправляем ответ с созданной заметкой
// status(201) - код 201 означает "Создано"
response.status(201).json({
success: true,
message: 'Заметка успешно создана',
data: newNote
});
});
// РОУТ 4: Обновление существующей заметки (UPDATE)
// Метод: PUT
// Адрес: /notes/:id
app.put('/notes/:id', function(request, response) {
/*
Эта функция обновляет существующую заметку
request.params.id - ID заметки для обновления
request.body - новые данные для заметки
*/
console.log('Получен запрос на обновление заметки с ID:', request.params.id);
const noteId = parseInt(request.params.id);
// Ищем индекс заметки в массиве
const noteIndex = notes.findIndex(function(note) {
return note.id === noteId;
});
// Если заметка не найдена
if (noteIndex === -1) {
console.log('Заметка для обновления не найдена');
return response.status(404).json({
try {
const result = await db.run(
'INSERT INTO notes (title, text, author) VALUES (?, ?, ?)',
[title, text, author]
);
const newNote = await db.get('SELECT * FROM notes WHERE id = ?', result.lastID);
res.status(201).json({
success: true,
message: 'Заметка успешно создана',
data: newNote
});
} catch (error) {
res.status(500).json({
success: false,
message: 'Заметка с таким ID не найдена'
message: 'Ошибка при создании заметки'
});
}
// Получаем данные из запроса
// Если какое-то поле не передано, используем старое значение
const updatedTitle = request.body.title || notes[noteIndex].title;
const updatedText = request.body.text || notes[noteIndex].text;
const updatedAuthor = request.body.author || notes[noteIndex].author;
// Обновляем заметку
notes[noteIndex] = {
...notes[noteIndex], // копируем все старые поля
title: updatedTitle, // обновляем заголовок
text: updatedText, // обновляем текст
author: updatedAuthor, // обновляем автора
updatedAt: new Date() // обновляем дату изменения
};
console.log('Заметка с ID', noteId, 'обновлена');
// Отправляем обновленную заметку
response.status(200).json({
success: true,
message: 'Заметка успешно обновлена',
data: notes[noteIndex]
});
});
// РОУТ 5: Удаление заметки (DELETE)
// Метод: DELETE
// Адрес: /notes/:id
app.delete('/notes/:id', function(request, response) {
/*
Эта функция удаляет заметку по ID
*/
console.log('Получен запрос на удаление заметки с ID:', request.params.id);
const noteId = parseInt(request.params.id);
// Ищем индекс заметки в массиве
const noteIndex = notes.findIndex(function(note) {
return note.id === noteId;
});
// Если заметка не найдена
if (noteIndex === -1) {
console.log('Заметка для удаления не найдена');
return response.status(404).json({
app.put('/notes/:id', async (req, res) => {
try {
const existingNote = await db.get('SELECT * FROM notes WHERE id = ?', req.params.id);
if (!existingNote) {
return res.status(404).json({
success: false,
message: 'Заметка с таким ID не найдена'
});
}
const updatedTitle = req.body.title || existingNote.title;
const updatedText = req.body.text || existingNote.text;
const updatedAuthor = req.body.author || existingNote.author;
await db.run(
'UPDATE notes SET title = ?, text = ?, author = ?, updatedAt = CURRENT_TIMESTAMP WHERE id = ?',
[updatedTitle, updatedText, updatedAuthor, req.params.id]
);
const updatedNote = await db.get('SELECT * FROM notes WHERE id = ?', req.params.id);
res.status(200).json({
success: true,
message: 'Заметка успешно обновлена',
data: updatedNote
});
} catch (error) {
res.status(500).json({
success: false,
message: 'Заметка с таким ID не найдена'
message: 'Ошибка при обновлении заметки'
});
}
// Удаляем заметку из массива
// splice(index, 1) удаляет 1 элемент начиная с позиции index
const deletedNote = notes.splice(noteIndex, 1)[0];
console.log('Заметка с ID', noteId, 'удалена');
// Отправляем подтверждение удаления
response.status(200).json({
success: true,
message: 'Заметка успешно удалена',
data: deletedNote
});
});
// РОУТ 7: Корневой маршрут (для проверки работы сервера)
// Метод: GET
// Адрес: /
app.get('/', function(_, response) {
/*
Простой маршрут для проверки, что сервер работает
*/
response.json({
app.delete('/notes/:id', async (req, res) => {
try {
const existingNote = await db.get('SELECT * FROM notes WHERE id = ?', req.params.id);
if (!existingNote) {
return res.status(404).json({
success: false,
message: 'Заметка с таким ID не найдена'
});
}
await db.run('DELETE FROM notes WHERE id = ?', req.params.id);
res.status(200).json({
success: true,
message: 'Заметка успешно удалена',
data: existingNote
});
} catch (error) {
res.status(500).json({
success: false,
message: 'Ошибка при удалении заметки'
});
}
});
app.get('/', (_, res) => {
res.json({
message: 'Добро пожаловать в API для заметок!',
endpoints: {
getAllNotes: 'GET /notes',
getOneNote: 'GET /notes/:id',
createNote: 'POST /notes',
updateNote: 'PUT /notes/:id',
deleteNote: 'DELETE /notes/:id',
docs: 'GET /docs'
},
instructions: 'Используйте Postman или curl для тестирования API'
deleteNote: 'DELETE /notes/:id'
}
});
});
// Экспортируем приложение для использования в server.js
export default app;