const express = require('express');
const sqlite3 = require('sqlite3').verbose();
const multer = require('multer');
const path = require('path');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;

const storage = multer.diskStorage({
  destination: (req, file, callback) => {
    callback(null, 'public/uploads/');
  },
  filename: (req, file, callback) => {
    callback(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
  },
});

const upload = multer({ storage });

const db = new sqlite3.Database('products.db');

db.serialize(() => {
  db.run('CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY, name TEXT, description TEXT, image TEXT, price REAL, category_id INTEGER)');
  db.run('CREATE TABLE IF NOT EXISTS categories (id INTEGER PRIMARY KEY, name TEXT)');
});

app.use(express.static('public/uploads/'));
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res) => {
  const page = req.query.page || 1;
  const itemsPerRow = 3;
  const itemsPerPage = 9;
  let currentPage = parseInt(page, 10);

  const selectedCategory = req.query.category;

  db.all('SELECT * FROM products', (err, rows) => {
    if (err) {
      return console.error(err.message);
    }

    db.all('SELECT * FROM categories', (err, categories) => {
      if (err) {
        return console.error(err.message);
      }

      const filteredRows = selectedCategory ? rows.filter(product => product.category_id == selectedCategory) : rows;

      const totalItems = filteredRows.length;
      const totalPages = Math.ceil(totalItems / itemsPerPage);

      const startIndex = (currentPage - 1) * itemsPerPage;
      const endIndex = currentPage * itemsPerPage;

      const productHtmlArray = filteredRows.slice(startIndex, endIndex).map((product, index) => `
          <div class="product">
              <img src="${product.image}" alt="${product.name}" class="product-image">
              <h2 class="product-title">${product.name.slice(0, 20)}${product.name.length > 20 ? '...' : ''}</h2>
              <p class "product-price">${product.price} руб</p>
              <button class="product-details-button" onclick="showDetails(${index})">Подробнее</button>
          </div>
      `);

      const rowsHtml = [];
      for (let i = 0; i < productHtmlArray.length; i += itemsPerRow) {
        rowsHtml.push('<div class="row">' + productHtmlArray.slice(i, i + itemsPerRow).join('') + '</div>');
      }

      const pageHtml = `
          <!DOCTYPE html>
          <html>
          <head>
              <meta name="viewport" content="width=device-width, initial-scale=1">
              <style>
                  body {
                      font-family: Arial, sans-serif;
                      margin: 0;
                      padding: 0;
                      background-color: #f0f0f0;
                  }
                  .container {
                      padding: 20px;
                  }
                  .row {
                      display: flex;
                      justify-content: space-between;
                      margin: 10px 0;
                  }
                  .product {
                      flex: 0 0 calc(33.33% - 20px);
                      background-color: #fff;
                      border: 3px solid #ccc;
                      border-radius: 10px;
                      text-align: center;
                      padding: 10px;
                      box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
                      height: 400px;
                  }
                  .product-image {
                      border-radius: 10%;
                      max-width: 100%;
                      max-height: 60%;
                  }
                  .product-title {
                      font-size: 18px;
                      margin: 10px 0;
                      white-space: nowrap;
                      overflow: hidden;
                      text-overflow: ellipsis;
                      max-width: 100%;
                  }
                  .product-price {
                      color: #0074d9;
                  }
                  .product-details-button {
                      background-color: #0074d9;
                      color: #fff;
                      border: none;
                      border-radius: 5px;
                      cursor: pointer;
                      margin-top: 10px;
                      padding: 5px 15px;
                      font-size: 14px;
                  }
                  .product-details-button:hover {
                      background-color: #0056b3;
                  }
                  .pagination {
                      text-align: center;
                      width: 100%;
                      margin-top: 20px;
                  }
                  .modal {
                      display: none;
                      position: fixed;
                      z-index: 1;
                      left: 0;
                      top: 0;
                      width: 100%;
                      height: 100%;
                      background-color: rgba(0, 0, 0, 0.4);
                  }
                  .modal-content {
                      background-color: #fff;
                      margin: 10% auto;
                      padding: 20px;
                      border: 1px solid #888;
                      width: 60%;
                      border-radius: 10px;
                      box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
                  }
                  .close {
                      float: right;
                      cursor: pointer;
                      font-size: 20px;
                  }
                  .product-details-title {
                      font-size: 24px;
                      margin: 10px 0;
                  }
                  .product-details-image {
                      border-radius: 10%;
                      max-width: 100%;
                  }
                  .product-details-description {
                      margin: 10px 0;
                  }
                  .product-details-price {
                      color: #0074d9;
                  }
                  .product-details-button {
                      background-color: #0074d9;
                      color: #fff;
                      border: none;
                      border-radius: 5px;
                      cursor: pointer;
                      margin-top: 10px;
                      padding: 5px 15px;
                      font-size: 14px;
                  }
                  .product-details-button:hover {
                      background-color: #0056b3;
                  }
                  .search-container {
                      display: flex;
                      justify-content: center;
                      margin: 20px 0;
                  }
                  .search-box {
                      width: 50%;
                      padding: 10px;
                      border: 1px solid #ccc;
                      border-radius: 5px;
                  }
              </style>
          </head>
          <body>
              <div class="container">
                  <div class="search-container">
                      <button id="categoriesButton" onclick="openCategoriesToolbar()">🪧 Категории</button>
                      <input type="text" id="searchBox" class="search-box" placeholder="Поиск...">
                      <button onclick="searchProducts()">🔎</button>
                  </div>
                  ${rowsHtml.join('')}
              </div>
              <div class="pagination">
                  <a href="/?page=${currentPage - 1}" class="pagination-button">Предыдущая</a>
                  <span>Страница ${currentPage} из ${totalPages}</span>
                  <a href="/?page=${currentPage + 1}" class="pagination-button">Следующая</a>
              </div>
              <div id="productDetails" class="modal">
                  <div class="modal-content">
                      <span class="close" onclick="closeDetails()">&times;</span>
                      <h2 class="product-details-title" id="productDetailsTitle"></h2>
                      <img class="product-details-image" id="productDetailsImage">
                      <p class="product-details-description" id="productDetailsDescription"></p>
                      <p class="product-details-price" id="productDetailsPrice"></p>
                      <button class="product-details-button" id="addToCartButton">Добавить в корзину</button>
                  </div>
              </div>
              <div id="categoriesToolbar" class="modal">
                  <div class="modal-content">
                      <span class="close" onclick="closeCategoriesToolbar()">&times;</span>
                      <h2>Категории</h2>
                      <ul id="categoryList">
                          ${categories.map(category => `<li><a href="/?category=${category.id}">${category.name}</a></li>`).join('')}
                      </ul>
                  </div>
              </div>
              <script>
                  function showDetails(index) {
                      const modal = document.getElementById('productDetails');
                      const title = document.getElementById('productDetailsTitle');
                      const imageElement = document.getElementById('productDetailsImage');
                      const descriptionElement = document.getElementById('productDetailsDescription');
                      const priceElement = document.getElementById('productDetailsPrice');
                      const addToCartButton = document.getElementById('addToCartButton');

                      const product = ${JSON.stringify(filteredRows)};
                      title.textContent = product[index].name;
                      imageElement.src = product[index].image;
                      descriptionElement.textContent = product[index].description;
                      priceElement.textContent = product[index].price + ' руб';

                      addToCartButton.onclick = () => {
                          alert('Товар добавлен в корзину');
                      }

                      modal.style.display = 'block';
                  }

                  function closeDetails() {
                      const modal = document.getElementById('productDetails');
                      modal.style.display = 'none';
                  }

                  function openCategoriesToolbar() {
                      const categoriesToolbar = document.getElementById('categoriesToolbar');
                      categoriesToolbar.style.display = 'block';
                  }

                  function closeCategoriesToolbar() {
                      const categoriesToolbar = document.getElementById('categoriesToolbar');
                      categoriesToolbar.style.display = 'none';
                  }

                  function searchProducts() {
                      const searchBox = document.getElementById('searchBox');
                      const searchTerm = searchBox.value.toLowerCase();
                      const productElements = document.querySelectorAll('.product');

                      productElements.forEach(element => {
                          const title = element.querySelector('.product-title').textContent.toLowerCase();
                          if (title.includes(searchTerm)) {
                              element.style.display = 'block';
                          } else {
                              element.style.display = 'none';
                          }
                      });
                  }
              </script>
          </body>
          </html>
      `;

      res.send(pageHtml);
    });
  });
});

app.get('/admin', (req, res) => {
  db.all('SELECT * FROM categories', (err, categories) => {
    if (err) {
      return console.error(err.message);
    }

    db.all('SELECT * FROM products', (err, products) => {
      if (err) {
        return console.error(err.message);
      }

      const adminPanelHtml = `
          <!DOCTYPE html>
          <html>
          <head>
              <meta name="viewport" content="width=device-width, initial-scale=1">
              <style>
                  body {
                      font-family: Arial, sans-serif;
                      margin: 0;
                      padding: 0;
                      background-color: #f0f0f0;
                  }
                  .container {
                      padding: 20px;
                  }
                  .admin-panel {
                      background-color: #fff;
                      border: 1px solid #ccc;
                      border-radius: 10px;
                      padding: 20px;
                      width: 90%;
                      max-width: 600px;
                      margin: 0 auto;
                      box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
                  }
                  .admin-panel h1 {
                      font-size: 24px;
                      margin-bottom: 20px;
                  }
                  .admin-input {
                      width: 100%;
                      padding: 10px;
                      margin: 10px 0;
                      border: 1px solid #ccc;
                      border-radius: 5px;
                  }
                  .admin-input[type="submit"] {
                      background-color: #0074d9;
                      color: #fff;
                      border: none;
                      border-radius: 5px;
                      cursor: pointer;
                      margin-top: 10px;
                  }
                  .admin-input[type="submit"]:hover {
                      background-color: #0056b3;
                  }
              </style>
          </head>
          <body>
              <div class="container">
                  <div class="admin-panel" id="adminPanel">
                      <h1>Админ-панель</h1>
                      <h2>Добавление товара</h2>
                      <form action="/add-product" method="post" enctype="multipart/form-data">
                          <input type="text" name="name" class="admin-input" placeholder="Название товара" required><br>
                          <input type="text" name="description" class="admin-input" placeholder="Описание товара" required><br>
                          <input type="file" name="image" class="admin-input" required><br>
                          <input type="number" name="price" class="admin-input" placeholder="Цена товара" required><br>
                          <select name="category" class="admin-input">
                              ${categories.map(category => `<option value="${category.id}">${category.name}</option>`).join('')}
                          </select>
                          <input type="submit" value="Добавить товар">
                      </form>
                      <h2>Добавление категории</h2>
                      <form action="/add-category" method="post">
                          <input type="text" name="category" class="admin-input" placeholder="Название категории" required><br>
                          <input type="submit" value="Добавить категорию">
                      </form>
                      <h2>Редактирование товаров</h2>
                      <form action="/edit-product" method="post">
                          <select name="product" class="admin-input">
                              ${products.map(product => `<option value="${product.id}">${product.name}</option>`).join('')}
                          </select>
                          <input type="text" name="newName" class="admin-input" placeholder="Новое название">
                          <input type="text" name="newDescription" class="admin-input" placeholder="Новое описание">
                          <input type="number" name="newPrice" class="admin-input" placeholder="Новая цена">
                          <select name="newCategory" class="admin-input">
                              ${categories.map(category => `<option value="${category.id}">${category.name}</option>`).join('')}
                          </select>
                          <input type="submit" value="Редактировать товар">
                      </form>
                      <h2>Удаление товара</h2>
                      <form action="/delete-product" method="post">
                          <select name="product" class="admin-input">
                              ${products.map(product => `<option value="${product.id}">${product.name}</option>`).join('')}
                          </select>
                          <input type="submit" value="Удалить товар" class="admin-input">
                      </form>
                      <h2>Удаление категории</h2>
                      <form action="/delete-category" method="post">
                          <select name="category" class="admin-input">
                              ${categories.map(category => `<option value="${category.id}">${category.name}</option>`).join('')}
                          </select>
                          <input type="submit" value="Удалить категорию" class="admin-input">
                      </form>
                  </div>
              </div>
          </body>
          </html>
      `;

      res.send(adminPanelHtml);
    });
  });
});

app.post('/add-product', upload.single('image'), (req, res) => {
  const name = req.body.name;
  const description = req.body.description;
  const image = '/uploads/' + req.file.filename;
  const price = parseFloat(req.body.price);
  const category_id = parseInt(req.body.category);

  const insertProduct = db.prepare('INSERT INTO products (name, description, image, price, category_id) VALUES (?, ?, ?, ?, ?)');
  insertProduct.run(name, description, image, price, category_id);
  insertProduct.finalize();

  res.redirect('/admin');
});

app.post('/add-category', (req, res) => {
  const category = req.body.category;

  const insertCategory = db.prepare('INSERT INTO categories (name) VALUES (?)');
  insertCategory.run(category);
  insertCategory.finalize();

  res.redirect('/admin');
});

app.post('/edit-product', (req, res) => {
  const productId = parseInt(req.body.product);
  const newName = req.body.newName;
  const newDescription = req.body.newDescription;
  const newPrice = parseFloat(req.body.newPrice);
  const newCategory = parseInt(req.body.newCategory);

  const updateProduct = db.prepare('UPDATE products SET name = ?, description = ?, price = ?, category_id = ? WHERE id = ?');
  updateProduct.run(newName, newDescription, newPrice, newCategory, productId);
  updateProduct.finalize();

  res.redirect('/admin');
});

app.post('/delete-product', (req, res) => {
  const productId = parseInt(req.body.product);

  const deleteProduct = db.prepare('DELETE FROM products WHERE id = ?');
  deleteProduct.run(productId);
  deleteProduct.finalize();

  res.redirect('/admin');
});

app.post('/delete-category', (req, res) => {
  const categoryId = parseInt(req.body.category);

  const deleteCategory = db.prepare('DELETE FROM categories WHERE id = ?');
  deleteCategory.run(categoryId);
  deleteCategory.finalize();

  res.redirect('/admin');
});

app.listen(port, () => {
  console.log(`Server started on http://localhost:${port}`);
});
Добавь красивые CSS стили жёлтых цветов на все элементы сайта