d3cc243686
Co-authored-by: Cursor <cursoragent@cursor.com>
311 lines
9.5 KiB
Markdown
311 lines
9.5 KiB
Markdown
# Shop
|
||
|
||
Интернет-магазин на **Node.js** с локальной базой **SQLite**.
|
||
|
||
## Возможности
|
||
|
||
- Каталог товаров с категориями и поиском
|
||
- Корзина и оформление заказа
|
||
- Регистрация и вход пользователей
|
||
- История заказов в личном кабинете
|
||
|
||
## Требования
|
||
|
||
- Node.js 18+
|
||
- npm
|
||
- На Linux для сборки `better-sqlite3`: `build-essential`, `python3`
|
||
|
||
---
|
||
|
||
## Быстрый развёртывание на Ubuntu
|
||
|
||
Скопируйте блок целиком на чистый сервер Ubuntu 22.04 / 24.04 (от root или через `sudo`):
|
||
|
||
```bash
|
||
# 1. Зависимости системы
|
||
apt update
|
||
apt install -y git curl build-essential python3
|
||
|
||
# 2. Node.js 20 LTS
|
||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
||
apt install -y nodejs
|
||
|
||
# 3. Клонирование (замените URL при необходимости)
|
||
cd /opt
|
||
git clone https://git.evilfox.cc/test/shop10.git shop
|
||
cd shop
|
||
|
||
# 4. Настройка окружения
|
||
cp .env.example .env
|
||
# Сгенерируйте секрет сессии:
|
||
sed -i "s/change-me-to-a-long-random-string/$(openssl rand -hex 32)/" .env
|
||
|
||
# 5. Установка приложения
|
||
npm install --omit=dev
|
||
|
||
# 6. Caddy (HTTPS + прокси) — см. раздел ниже; для проверки без домена:
|
||
npm start
|
||
```
|
||
|
||
Без Caddy сайт на **http://IP:3000**. С Caddy и доменом — **https://ваш-домен**.
|
||
|
||
В `.env` для production задайте (уже есть в `.env.example`):
|
||
|
||
```env
|
||
PORT=3000
|
||
HOST=127.0.0.1
|
||
NODE_ENV=production
|
||
TRUST_PROXY=1
|
||
SESSION_SECRET=ваш-длинный-секрет
|
||
```
|
||
|
||
`HOST=127.0.0.1` — Node слушает только localhost; снаружи доступ через Caddy.
|
||
|
||
При первом запуске создаются `data/shop.db`, `data/sessions.db` и демо-товары.
|
||
|
||
---
|
||
|
||
## Запуск как служба (systemd)
|
||
|
||
Чтобы магазин работал после перезагрузки сервера:
|
||
|
||
```bash
|
||
cp /opt/shop/deploy/shop.service /etc/systemd/system/shop.service
|
||
|
||
# Код обновляйте от root; НЕ делайте chown -R на весь /opt/shop (ломает git pull)
|
||
cd /opt/shop
|
||
git config --global --add safe.directory /opt/shop
|
||
git pull
|
||
npm install --omit=dev
|
||
|
||
# Запись только в data/ — для пользователя службы www-data
|
||
mkdir -p /opt/shop/data
|
||
chown -R www-data:www-data /opt/shop/data
|
||
chmod +x /opt/shop/scripts/diagnose-502.sh 2>/dev/null || true
|
||
|
||
systemctl daemon-reload
|
||
systemctl enable shop
|
||
systemctl start shop
|
||
systemctl status shop
|
||
|
||
# Backend должен ответить:
|
||
curl -s http://127.0.0.1:3000/health
|
||
# {"ok":true,"service":"shop"}
|
||
```
|
||
|
||
Логи: `journalctl -u shop -f`
|
||
|
||
---
|
||
|
||
## Caddy — SSL и reverse proxy (рекомендуется)
|
||
|
||
[Caddy](https://caddyserver.com/) автоматически выпускает и продлевает сертификаты Let's Encrypt.
|
||
|
||
**Перед установкой:**
|
||
|
||
1. Домен указывает на IP сервера (A-запись).
|
||
2. Открыты порты **80** и **443** в файрволе.
|
||
3. Служба `shop` запущена и слушает `127.0.0.1:3000`.
|
||
|
||
### Установка Caddy на Ubuntu
|
||
|
||
```bash
|
||
apt install -y debian-keyring debian-archive-keyring apt-transport-https
|
||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
|
||
| gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
|
||
| tee /etc/apt/sources.list.d/caddy-stable.list
|
||
apt update
|
||
apt install -y caddy
|
||
```
|
||
|
||
### Конфигурация
|
||
|
||
В репозитории лежит пример: `caddy/Caddyfile.example`.
|
||
|
||
```bash
|
||
# Замените shop.example.com и email на свои
|
||
cp /opt/shop/caddy/Caddyfile.example /etc/caddy/Caddyfile
|
||
nano /etc/caddy/Caddyfile
|
||
```
|
||
|
||
Пример `/etc/caddy/Caddyfile`:
|
||
|
||
```caddyfile
|
||
{
|
||
email admin@example.com
|
||
}
|
||
|
||
shop.example.com {
|
||
encode gzip zstd
|
||
reverse_proxy 127.0.0.1:3000
|
||
}
|
||
```
|
||
|
||
Проверка и перезапуск:
|
||
|
||
```bash
|
||
caddy validate --config /etc/caddy/Caddyfile
|
||
systemctl enable caddy
|
||
systemctl reload caddy
|
||
systemctl status caddy
|
||
```
|
||
|
||
Сайт: **https://shop.example.com**
|
||
|
||
Логи Caddy: `journalctl -u caddy -f`
|
||
|
||
### Файрвол (ufw)
|
||
|
||
```bash
|
||
ufw allow 22/tcp
|
||
ufw allow 80/tcp
|
||
ufw allow 443/tcp
|
||
ufw enable
|
||
```
|
||
|
||
Порт **3000** наружу не открывайте — к приложению ходят только через Caddy.
|
||
|
||
### Порядок запуска после перезагрузки
|
||
|
||
1. `shop` (Node.js на `127.0.0.1:3000`)
|
||
2. `caddy` (прокси + HTTPS)
|
||
|
||
### HTTP 502 при рабочем SSL
|
||
|
||
**SSL есть, 502 — значит Caddy жив, а Node на `127.0.0.1:3000` не отвечает.**
|
||
|
||
На сервере выполните:
|
||
|
||
```bash
|
||
bash /opt/shop/scripts/diagnose-502.sh
|
||
journalctl -u shop -n 50 --no-pager
|
||
curl -v http://127.0.0.1:3000/health
|
||
```
|
||
|
||
**Частые причины и исправление:**
|
||
|
||
| Причина | Что сделать |
|
||
|--------|-------------|
|
||
| Служба `shop` не запущена или падает | `systemctl restart shop`, смотрите логи `journalctl -u shop -f` |
|
||
| Нет `npm install` / сломан `better-sqlite3` | `cd /opt/shop && npm install --omit=dev` (нужны `build-essential`, `python3`) |
|
||
| Нет прав на `data/` у `www-data` | `mkdir -p /opt/shop/data && chown -R www-data:www-data /opt/shop/data` |
|
||
| `dubious ownership` / нет `git pull` | `chown -R root:root /opt/shop` + `safe.directory` (см. выше) |
|
||
| В `.env` нет `HOST`/`PORT` | `HOST=127.0.0.1`, `PORT=3000`, затем `systemctl restart shop` |
|
||
| Неверный путь к `node` в systemd | `which node` → подставьте в `ExecStart` в `/etc/systemd/system/shop.service` |
|
||
| Caddy стартовал раньше shop | `cp deploy/caddy-after-shop.conf /etc/systemd/system/caddy.service.d/shop.conf` и `daemon-reload` |
|
||
|
||
**Быстрое восстановление:**
|
||
|
||
```bash
|
||
cd /opt/shop
|
||
git config --global --add safe.directory /opt/shop
|
||
git pull
|
||
npm install --omit=dev
|
||
mkdir -p data
|
||
chown -R www-data:www-data /opt/shop/data
|
||
systemctl restart shop
|
||
curl -s http://127.0.0.1:3000/health # должен быть {"ok":true,...}
|
||
systemctl reload caddy
|
||
```
|
||
|
||
**Ошибка `dubious ownership` при `git pull`:**
|
||
|
||
Вы сделали `chown -R www-data` на весь каталог. Верните владельца репозиторию root и оставьте `data/` за www-data:
|
||
|
||
```bash
|
||
chown -R root:root /opt/shop
|
||
chown -R www-data:www-data /opt/shop/data
|
||
git config --global --add safe.directory /opt/shop
|
||
git pull
|
||
```
|
||
|
||
Пока `curl http://127.0.0.1:3000/health` не возвращает OK — HTTPS через Caddy будет отдавать 502.
|
||
|
||
### Другие проблемы
|
||
|
||
| Симптом | Решение |
|
||
|--------|---------|
|
||
| Нет сертификата | DNS, порты 80/443, верный домен в `Caddyfile` |
|
||
| Редирект-цикл / нет cookies | В `.env`: `TRUST_PROXY=1`, `NODE_ENV=production` |
|
||
|
||
---
|
||
|
||
## Обновление с Git
|
||
|
||
```bash
|
||
cd /opt/shop
|
||
systemctl stop shop
|
||
git pull
|
||
npm install --omit=dev
|
||
systemctl start shop
|
||
# Caddy перезагружать не нужно, если Caddyfile не менялся
|
||
```
|
||
|
||
---
|
||
|
||
## Локальная разработка
|
||
|
||
```bash
|
||
npm install
|
||
cp .env.example .env
|
||
npm run dev
|
||
```
|
||
|
||
Сайт: [http://localhost:3000](http://localhost:3000)
|
||
|
||
## Переменные окружения
|
||
|
||
| Переменная | Описание | По умолчанию |
|
||
|------------------|---------------------------|----------------|
|
||
| `PORT` | Порт HTTP-сервера | `3000` |
|
||
| `HOST` | Адрес привязки | `0.0.0.0` (dev), `127.0.0.1` (prod) |
|
||
| `NODE_ENV` | Режим (`production` — secure cookies) | — |
|
||
| `TRUST_PROXY` | Доверять заголовкам Caddy (`1`) | — |
|
||
| `SESSION_SECRET` | Секрет для сессий | dev-значение |
|
||
|
||
## Скрипты npm
|
||
|
||
| Команда | Описание |
|
||
|----------------|----------------------------------|
|
||
| `npm start` | Запуск сервера (production) |
|
||
| `npm run dev` | Запуск с автоперезагрузкой |
|
||
| `npm run seed` | Заполнение каталога, если пуст |
|
||
|
||
## База данных
|
||
|
||
Локально в каталоге `data/`:
|
||
|
||
- `shop.db` — товары, пользователи, заказы
|
||
- `sessions.db` — сессии
|
||
|
||
Каталог `data/` в git не попадает (см. `.gitignore`). Делайте резервные копии:
|
||
|
||
```bash
|
||
cp -a data/shop.db data/shop.db.bak
|
||
```
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
caddy/
|
||
Caddyfile.example — пример reverse proxy + SSL
|
||
deploy/
|
||
shop.service — unit для systemd
|
||
scripts/
|
||
diagnose-502.sh — проверка при 502
|
||
src/
|
||
server.js — точка входа
|
||
db.js — схема SQLite
|
||
seed.js — демо-данные
|
||
routes/ — маршруты
|
||
views/ — шаблоны EJS
|
||
public/css/ — стили
|
||
```
|
||
|
||
## Репозиторий
|
||
|
||
```bash
|
||
git clone https://git.evilfox.cc/test/shop10.git
|
||
```
|