diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..d14baf7
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,5 @@
+/node_modules
+
+npm-debug.log
+yarn-debug.log*
+yarn-error.log*
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..93a55d4
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,11 @@
+FROM node:10
+
+WORKDIR /usr/src/app
+
+COPY package*.json ./
+
+RUN npm install
+
+COPY . .
+
+CMD ["npm", "start"]
\ No newline at end of file
diff --git a/README.md b/README.md
index a80ade3..ec47f18 100644
--- a/README.md
+++ b/README.md
@@ -27,281 +27,45 @@
-## Содержание
-- [О проекте](#О-проекте)
-- [Структура репозитория](#Структура-репозитория)
-- [Установка и настройка скрипта](#Установка-и-настройка-скрипта)
-- [Запуск скрипта на локальном компьютере](#Запуск-скрипта-на-локальном-компьютере)
-- [Запуск скрипта на удаленном сервере](#Запуск-скрипта-на-удаленном-сервере)
-- [Выгрузка скрипта на бесплтаный хостинг от Heroku](#Выгрузка-скрипта-на-бесплтаный-хостинг-от-Heroku)
-- [Переменные среды](#Переменные-среды)
-- [API](#API)
-- [Требования](#Требования)
-- [Лицензия](#Лицензия)
+## 🐬 Установка через Docker:
-## О проекте:
-Этот проект представляет из себя интернет-магазин на платформе NodeJS, Вы можете продавать свои цифровые товары именно загрузив этот репозиторий, в [панели управления](#Панель-управления) Вы сможете создавать, удалять и редактировать товары или категории . В проекте используется концепция REST API, поэтому для отдачи информации клиенту существует отдельный API. Информация о покупателях, товарах, категориях и т.д хранится в БД MongoDB (используется нативный драйвер).
-### Функционал покупателя:
-- Возможность выбирать интересующие категории, переход по одиночным страницам конкретного товара.
-- Возможность комментировать как отдельный товар, так и весь магазин на отдельной странице с помощью системы комментариев Disqus.
-- Возможность фильтровать интересующую категорию или все товары через фильтр.
-- Покупатель может совершать одну или несколько покупок через корзину или кнопку "Купить".
-- Оплата осуществляется через сервис QIWI API.
-- Результатом оплаты будут данные одного или нескольких товаров, все данные отсылаются на электронную почту покупателя, которую он указал при создании заказа.
-- Покупатель может узнать все свои совершенные покупки в магазине на отдельной странице.
-### Функционал администратора:
-Для входа в админ-панель необходимо дописать в адресной строке `/admin`, после Вы попадете на страницу авторизации где нужно ввести данные в формате `email:password`. По умолчанию `admin@support.ru:admin`
-- Пароль админа хэшируется через `bcrypt` для безопасности и записывается в БД.
-- На главной странице есть небольшая статистика, где можно посмотреть: сумму проданных товаров, кол-во проданных товаров, кол-во товаров в наличии.
-- На главной странце так же можно узнать подробные данные о покупателях: номер заказа, почта покупателя, способ оплаты, дата покупки, сумма и данные которые были высланы по почте.
-- Возможность находить конкретного покупателя через поле (нужно знать номер заказа).
-- Возможность добавлять, удалять или редактировать товары на странице "Товары", так же можно посмотреть и информацию о товаре.
-- Поиск товаров через поле поиска (нужно знать частичное или полное название товара).
-- Добавление, удаление и редактирование категорий через страницу "Категории", каждой категории присваивается собственная иконка, которую можно загрузить через страницу "Иконки".
-- Возможность править контент, изменять общее оформление магазина (только цвет).
-- Возможность изменять данные администратора: почта, пароль, имя.
-
-## Структура репозитория:
-В основе всего репозитория существуют две ветки. Ветка master предназначена для разработки приложения, для выгрузки готового приложения на сервер используйте ветку production.
-
-## Установка и настройка скрипта:
-
-### Запуск скрипта на локальном компьютере:
-- [Установите последний релиз Digital Store](https://github.com/sergeyyarkov/digital-store/releases) из GitHub репозитория.
-- Или клонировать репозиторий `git clone https://github.com/sergeyyarkov/digital-store.git -b production`.
-- Установите [Node.js](https://nodejs.org/ru/) не ниже версии 7.6.0, в процессе установки Node.js установите Node Package Manager.
-- Установите БД MongoDB на ваш компьютер или создайте кластер на сайте [www.mongodb.com](https://www.mongodb.com/) и импортируйте туда дамп структуры БД, которые находится в корне приложения `/database_dump`.
-- Для работоспособности интернет-магазина необходимо установить зависимости. Выполните комманду `npm i`.
-- Далле необходимо настроить переменные среды. Создайте файл `.env` в корне вашего интернет-магазина и запишите туда [значения для переменных](#Переменные-среды). Обратите внимание, что для выгрузки скрипта на production сервер, нужно устанавливать переменные среды для вашего сервера, файл `.env` создавать не нужно.
-- После этого, введите `npm run start` и перейдите по адресу `localhost:3000`, вы увидите уже рабочий интернет-магазин который запущен у вас на локальном компьютере.
-
-### Запуск скрипта на удаленном сервере:
-В данном примере на удаленном сервере будет использоваться связка `Ubuntu 18.04, Nginx, MongoDB, PM2, NodeJS` + `SSL Lets Encrypt`
-
-- Переходим на сайт [RUVDS.COM](https://ruvds.com/pr4435) и приобретаем услугу VPS-хостинга.
-- Подключаемся к серверу по SSH и следуем далее описанным шагам.
-- Устанавливаем NodeJS:
-
-```bash
-apt-get update && apt-get install curl -y
-curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
-export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
-[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
-source ~/.bashrc
-nvm install 12
-```
-Если при выполнении команд `node -v` и `npm -v` выдается версия, то идем дальше.
-- Создаем папку `/apps`, после переходим в нее и клонируем проект с github и переходим в наш магазин:
-
-```bash
-mkdir apps
-
-cd apps/
-
-git clone https://github.com/sergeyyarkov/digital-store.git -b production
-
-cd digital-store/
-```
-- Устанавливаем зависимости для интернет-магазина `npm install`.
-- Устанавливаем MongoDB:
-
-```bash
-apt install -y mongodb
-
-systemctl status mongodb <--- проверяем состояние службы, должно быть active (running)
-```
-Теперь нужно дать доступ для подключения к БД именно с вашего компьютера, это нам понадобится для импортирования дампа БД:
-ip адресс берете с сайта 2ip
-
-```bash
-ufw enable
-
-ufw allow from ваш_ip_адресс_компьютера/32 to any port 27017
-```
-- Далее открываем конфигурационный файл MongoDB: `nano /etc/mongodb.conf`:
-Добавляем IP-адрес вашего сервера в значение bindIP:
-
-```bash
-logappend=true
-
-bind_ip = 127.0.0.1,your_server_ip
-#port = 27017
-```
-
-- Сохраните файл, закройте редактор и перезапустите MongoDB: `systemctl restart mongodb`:
-- Теперь доступ к БД есть только у вашего компьютера, далее скачиваем [Studio 3T](https://studio3t.com/) и создаем новое соединение, ip адресс указываем вашего сервера.
-- После подключения, создаем новую БД и называем её как хотим.
-- Кликаем по вашей созданной БД и вверху нажимаем `Import`, после выбираем все JSON файлы, которые хранятся в папке `/database_dump`.
-- Если все 6 коллекций успешно импортировались, то идем дальше.
-- Устанавливаем менеджер процессов PM2 `npm install -g pm2`.
-- Редактируем файл с переменными в файле ecosystem.config.js и устанавливаем свои значения `nano ecosystem.config.js`.
-- Далее запускаем наше приложение:
-
-```bash
-pm2 start ecosystem.config.js --env production <--- стартуем наше приложение
-
-pm2 status <--- как узнать статус запущенных процессов
-
-pm2 save <--- сохраняем процесс
-
-pm2 startup ubuntu <--- автоматически запускаем наше приложение в случае рестарта сервера
-```
-
-- Настраиваем файрволл:
-
-```bash
-ufw allow ssh
-
-ufw allow http
-
-ufw allow https
-```
-
-- Далее устанавливаем веб-сервер nginx `apt install nginx`, после редактируем конфигурационный файл `nano /etc/nginx/sites-available/default`:
-
-```bash
-server_name yourdomain.com www.yourdomain.com;
-
- location / {
- proxy_pass http://localhost:3000; #порт запущенного приложения
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection 'upgrade';
- proxy_set_header Host $host;
- proxy_cache_bypass $http_upgrade;
- }
-```
-
-- Обязательно настройте gzip сжатие, ваш сайт будет работать быстрее:
-
-```bash
-server {
- gzip on;
- gzip_disable "msie6";
- gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
-}
-```
-
-- Так же желательно избавиться от дубля страницы www:
-
-```bash
-if ($host = 'www.example.com') {
- return 301 https://example.com$request_uri;
-}
-```
-
-- После настройки проверьте ваш конфигурационный файл на ошибки `nginx -t`, если ошибок нет, то перезапускаем сервис nginx `service nginx restart`.
-- На сайте RUDVS добавьте свой домен и привяжите его к своему серверу, настройте DNS записи у регистратора. Через какое то время ваш магазин будет доступен по домену.
-- Далее настройте SSL сертификат:
-
-```bash
-add-apt-repository ppa:certbot/certbot
-
-apt-get update
-
-apt-get install python-certbot-nginx
-
-certbot --nginx -d yourdomain.com <--- в процессе установки выбираем цифру 2
-```
-
-- Ваш конфигурационный файл должен выглядеть примерно так:
-
-```bash
-server {
-
- gzip on;
- gzip_disable "msie6";
- gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
-
- root /var/www/html;
-
- index index.html index.htm index.nginx-debian.html;
-
- server_name yourdomain.ru www.yourdomain.ru;
-
- location / {
- proxy_pass http://localhost:3000;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection 'upgrade';
- proxy_set_header Host $host;
- proxy_cache_bypass $http_upgrade;
- }
-
- if ($host = 'www.yourdomain.ru') {
- return 301 https://yourdomain.ru$request_uri;
- }
-
- listen [::]:443 ssl ipv6only=on; # managed by Certbot
- listen 443 ssl; # managed by Certbot
- ssl_certificate /etc/letsencrypt/live/yourdomain.ru/fullchain.pem; # managed by Certbot
- ssl_certificate_key /etc/letsencrypt/live/yourdomain.ru/privkey.pem; # managed by Certbot
- include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
- ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
-}
-
-server {
- if ($host = www.yourdomain.ru) {
- return 301 https://$host$request_uri;
- } # managed by Certbot
-
-
- if ($host = yourdomain.ru) {
- return 301 https://$host$request_uri;
- } # managed by Certbot
-
- listen 80 default_server;
- listen [::]:80 default_server;
-
- server_name yourdomain.ru www.yourdomain.ru;
- return 404; # managed by Certbot
-}
-
-```
-
-### Установка скрипта на бесплатный хостинг от Heroku:
-- Регистрируемся на сайте [Heroku](https://signup.heroku.com/login).
-- Устанавливаем heroku-cli `https://devcenter.heroku.com/articles/heroku-cli`.
-- Проверьте установку, написав в консоле `heroku --help` если всё выводит, идем дальше.
-- Залогиньтесь: `heroku login`.
-- Создайте новый проект Herkou: `heroku create`.
-- Создайте кластер на сайте [www.mongodb.com](https://www.mongodb.com/) и импортируйте туда дамп структуры БД, который находится в корне приложения `/database_dump`. При подключении к БД выберите метод `Connect Your Application`
-- Переходим на сайт heroku, идем в свое созданное приложение и открываем вкладку `Settings -> Config Vars` кнопка `Reveal Config Vars` и настраиваем [переменные среды](#Переменные-среды).
-- Создаем папку с любым названием, открываем её и клонируем проект с github: `git clone https://github.com/sergeyyarkov/digital-store.git -b production .`
-- Сменим ветку на master: `git checkout -b master`
-- Удаляем файл ecosystem.config.js (в случае с heroku он не нужен т.к переменные мы храним у heroku)
-- Добавляем удаленный репозиторий `heroku git:remote -a название вашего приложения`.
-- Деплой:
-```bash
-git add .
-git commit -m "make it better"
-git push heroku master
-```
+-
### Настройка платежной системы:
+
В скрипте на данный момент доступна система оплаты через сервис QIWI, чтобы настроить оплату в вашем магазине следуйте следующим шагам:
+
- Перейдите на сайт [QIWI API](https://qiwi.com/p2p-admin/transfers/api) и создайте пару ключей авторизации. Сохраните секретный ключ.
- Если вы хотите настроить оформление формы приемов перевода, то перейдите [сюда](https://qiwi.com/p2p-admin/transfers/link) и скопируйте значение переменной `themeCode` и вставьте это значение по адресу `config/routes/payments/qiwi.payment.js`.
- Секретный ключ нужно указать в поле переменной среды `QIWI_SECRET_KEY`.
- Редирект после оплаты товара через QIWI на локальный адрес `localhost:3000` работать не будет, необходим домен.
### Настройка почтового сервиса:
+
Для отдачи данных товара на почту покупателя после покупки товара, нужно настроить nodemailer:
+
- Настройте почтовый сервис по адресу `config/nodemailer.config.js`:
+
```javascript
host: "smtp.mail.ru",
port: 465,
secure: true, // true для 465, false для остальных.
```
+
- Затем укажите переменные среды `EMAIL_LOGIN` и `EMAIL_PASSWORD`.
### Настройка системы комментариев:
+
В интернет магазине используется система комментариев [Disqus](https://disqus.com/):
+
- Добавьте свой домен на их сайт и замените embed код по адресу `dist/public/js/disqus.js`.
## Переменные среды:
+
Перменные среды нужны для работоспособности всего приложения, очень важно вписать верные данные для избежания возможных ошибок.
+
### Описание переменных:
+
| Переменная | Описание |
| --------------- | -------------------------------------------------------- |
| DATABASE_URI | Подключение к БД MongoDB |
@@ -311,6 +75,7 @@ secure: true, // true для 465, false для остальных.
| QIWI_SECRET_KEY | Секретный ключ QIWI для работоспособности системы оплаты |
`.env` файл:
+
```bash
DATABASE_URI=
DATABASE_NAME=
@@ -318,161 +83,186 @@ QIWI_SECRET_KEY=
EMAIL_LOGIN=
EMAIL_PASSWORD=
```
-
+
## API:
+
### API представляет из себя различные URL, которые отвечают данными только в формате JSON. Здесь описаны основные `GET` запросы на сервер для получения данных:
+
#### Для просмотра всех товаров которые существуют в магазине отправляется запрос:
-```bash
-https://store-name.com/api/items
+
+```bash
+https://store-name.com/api/items
```
+
Ответ:
+
```javascript
-[
+;[
{
- _id: "5e15dbc415303a1980cd77a0",
- title: "Тестовый товар №1",
+ _id: '5e15dbc415303a1980cd77a0',
+ title: 'Тестовый товар №1',
count: 0,
price: 1,
- category: "steam",
- description: "-",
- date: "2020-01-08T13:40:20.353Z"
+ category: 'steam',
+ description: '-',
+ date: '2020-01-08T13:40:20.353Z',
},
{
- _id: "5e15dcb4138c3339bcbc3719",
- title: "Тестовый товар №2",
+ _id: '5e15dcb4138c3339bcbc3719',
+ title: 'Тестовый товар №2',
count: 0,
price: 1,
- category: "uplay",
- description: "-",
- date: "2020-01-08T13:44:20.940Z"
+ category: 'uplay',
+ description: '-',
+ date: '2020-01-08T13:44:20.940Z',
},
]
```
### Для просмотра товаров в интересующей категории отправляется запрос:
+
```bash
https://store-name.com/api/items/{category}
```
+
Ответ:
+
```javascript
-[
+;[
{
- _id: "5e15dbc415303a1980cd77a0",
- title: "Тестовый товар №1",
+ _id: '5e15dbc415303a1980cd77a0',
+ title: 'Тестовый товар №1',
count: 0,
price: 1,
- category: "steam",
- description: "-",
- date: "2020-01-08T13:40:20.353Z"
+ category: 'steam',
+ description: '-',
+ date: '2020-01-08T13:40:20.353Z',
},
{
- _id: "5e1ccf30d5b894228c86385a",
- title: "Тестовый товар №2",
+ _id: '5e1ccf30d5b894228c86385a',
+ title: 'Тестовый товар №2',
count: 10,
price: 1,
- category: "steam",
- description: "-",
- date: "2020-01-13T20:12:32.814Z"
- }
+ category: 'steam',
+ description: '-',
+ date: '2020-01-13T20:12:32.814Z',
+ },
]
```
### Для просмотра информации о всех категориях отправляется запрос:
+
```bash
https://store-name.com/api/categories
```
+
Ответ:
+
```javascript
-[
+;[
{
- _id: "5df64563309e691f2c33d400",
- title: "uplay",
- img: "img-1576349954743.svg",
- type: "type",
- format: "format"
+ _id: '5df64563309e691f2c33d400',
+ title: 'uplay',
+ img: 'img-1576349954743.svg',
+ type: 'type',
+ format: 'format',
},
{
- _id: "5e04ea023625771abc75fd14",
- title: "fortnite",
- img: "img-1576349989720.svg",
- type: "type",
- format: "format"
+ _id: '5e04ea023625771abc75fd14',
+ title: 'fortnite',
+ img: 'img-1576349989720.svg',
+ type: 'type',
+ format: 'format',
},
]
```
### Для просмотра информации о интересующей категории отправляется запрос:
+
```bash
https://store-name.com/api/category/{category}
```
+
Ответ:
+
```javascript
-[
+;[
{
- _id: "5df64563309e691f2c33d400",
- title: "uplay",
- img: "img-1576349954743.svg",
- type: "type",
- format: "format"
- }
+ _id: '5df64563309e691f2c33d400',
+ title: 'uplay',
+ img: 'img-1576349954743.svg',
+ type: 'type',
+ format: 'format',
+ },
]
```
### Для просмотра данных всех товаров отправляется запрос: **Нужна авторизация для администратора.**
+
```bash
https://store-name.com/api/items/data
```
+
Ответ:
+
```javascript
-[
+;[
{
- _id: "5e15dbc415303a1980cd77a0",
- title: "Тестовый товар №1",
- data: ["login:pass", "login:pass", "login:pass"]
+ _id: '5e15dbc415303a1980cd77a0',
+ title: 'Тестовый товар №1',
+ data: ['login:pass', 'login:pass', 'login:pass'],
},
{
- _id: "5e175c63b479600ac493ce88",
- title: "Тестовый товар №2",
- data: ["login:pass", "login:pass", "login:pass"]
- }
+ _id: '5e175c63b479600ac493ce88',
+ title: 'Тестовый товар №2',
+ data: ['login:pass', 'login:pass', 'login:pass'],
+ },
]
```
### Для просмотра покупателей отправляется запрос: **Нужна авторизация для администратора.**
+
```bash
https://store-name.com/api/buyers
```
+
Ответ:
+
```javascript
-[
+;[
{
- _id: "5e1c97701b619a31f010062c",
- bill_id: "9244e5ae-560a-432a-9882-bc1b941c8cca",
- email: "test-email@mail.com",
- method: "qiwi",
- date: "2020-01-13T18:13:38.623+03:00",
+ _id: '5e1c97701b619a31f010062c',
+ bill_id: '9244e5ae-560a-432a-9882-bc1b941c8cca',
+ email: 'test-email@mail.com',
+ method: 'qiwi',
+ date: '2020-01-13T18:13:38.623+03:00',
amount: 2,
- data: ["login:pass", "login:pass"]
- }
+ data: ['login:pass', 'login:pass'],
+ },
]
```
### Для просмотра списка иконок отправляется запрос: **Нужна авторизация для администратора.**
+
```bash
https://store-name.com/api/icons
```
+
Ответ:
+
```javascript
-[
- "img-1576349954743.svg",
- "img-1576349989720.svg",
- "img-1576420937629.svg",
- "img-1576421960189.svg"
+;[
+ 'img-1576349954743.svg',
+ 'img-1576349989720.svg',
+ 'img-1576420937629.svg',
+ 'img-1576421960189.svg',
]
```
## Требования:
+
- Node.js v7.6.0 или выше
## Лицензия:
+
[MIT](https://github.com/sergeyyarkov/digital-store/blob/master/LICENSE)
diff --git a/data/backup/digital_store/administrators.bson b/data/backup/digital_store/administrators.bson
new file mode 100644
index 0000000..865c777
Binary files /dev/null and b/data/backup/digital_store/administrators.bson differ
diff --git a/data/backup/digital_store/administrators.metadata.json b/data/backup/digital_store/administrators.metadata.json
new file mode 100644
index 0000000..56476dd
--- /dev/null
+++ b/data/backup/digital_store/administrators.metadata.json
@@ -0,0 +1 @@
+{"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"digital_store.administrators"}],"uuid":"7c7a24d1f1984eb3823ca3af77c8f887"}
\ No newline at end of file
diff --git a/data/backup/digital_store/buyers.bson b/data/backup/digital_store/buyers.bson
new file mode 100644
index 0000000..e69de29
diff --git a/data/backup/digital_store/buyers.metadata.json b/data/backup/digital_store/buyers.metadata.json
new file mode 100644
index 0000000..2954237
--- /dev/null
+++ b/data/backup/digital_store/buyers.metadata.json
@@ -0,0 +1 @@
+{"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"digital_store.buyers"}],"uuid":"4e3bebb2aa9648a5ba18b6dc30b2b2e8"}
\ No newline at end of file
diff --git a/data/backup/digital_store/categories.bson b/data/backup/digital_store/categories.bson
new file mode 100644
index 0000000..b9aced5
Binary files /dev/null and b/data/backup/digital_store/categories.bson differ
diff --git a/data/backup/digital_store/categories.metadata.json b/data/backup/digital_store/categories.metadata.json
new file mode 100644
index 0000000..7ad10a6
--- /dev/null
+++ b/data/backup/digital_store/categories.metadata.json
@@ -0,0 +1 @@
+{"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"digital_store.categories"}],"uuid":"73f3159b00974d27a9c03a507b4734dd"}
\ No newline at end of file
diff --git a/data/backup/digital_store/content.bson b/data/backup/digital_store/content.bson
new file mode 100644
index 0000000..503663b
Binary files /dev/null and b/data/backup/digital_store/content.bson differ
diff --git a/data/backup/digital_store/content.metadata.json b/data/backup/digital_store/content.metadata.json
new file mode 100644
index 0000000..10e9d26
--- /dev/null
+++ b/data/backup/digital_store/content.metadata.json
@@ -0,0 +1 @@
+{"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"digital_store.content"}],"uuid":"3f734f39ee614324be532246ad14faf3"}
\ No newline at end of file
diff --git a/data/backup/digital_store/info.bson b/data/backup/digital_store/info.bson
new file mode 100644
index 0000000..2497809
Binary files /dev/null and b/data/backup/digital_store/info.bson differ
diff --git a/data/backup/digital_store/info.metadata.json b/data/backup/digital_store/info.metadata.json
new file mode 100644
index 0000000..dd8b0c2
--- /dev/null
+++ b/data/backup/digital_store/info.metadata.json
@@ -0,0 +1 @@
+{"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"digital_store.info"}],"uuid":"33f3568c85fe4350bfae3aec7059bd91"}
\ No newline at end of file
diff --git a/data/backup/digital_store/items.bson b/data/backup/digital_store/items.bson
new file mode 100644
index 0000000..ba026a3
Binary files /dev/null and b/data/backup/digital_store/items.bson differ
diff --git a/data/backup/digital_store/items.metadata.json b/data/backup/digital_store/items.metadata.json
new file mode 100644
index 0000000..4bb1a0e
--- /dev/null
+++ b/data/backup/digital_store/items.metadata.json
@@ -0,0 +1 @@
+{"options":{},"indexes":[{"v":{"$numberInt":"2"},"key":{"_id":{"$numberInt":"1"}},"name":"_id_","ns":"digital_store.items"},{"v":{"$numberInt":"2"},"key":{"_fts":"text","_ftsx":{"$numberInt":"1"}},"name":"title_text","language_override":"language","ns":"digital_store.items","weights":{"title":{"$numberInt":"1"}},"default_language":"english","textIndexVersion":{"$numberInt":"3"}}],"uuid":"14bff4a55fcf4fd2963b07f75add3ae8"}
\ No newline at end of file
diff --git a/data/nginx/app.conf b/data/nginx/app.conf
new file mode 100644
index 0000000..a01c4cd
--- /dev/null
+++ b/data/nginx/app.conf
@@ -0,0 +1,20 @@
+server {
+ listen 80;
+
+ gzip on;
+ gzip_disable "msie6";
+ gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
+
+ server_name localhost;
+ server_tokens off;
+
+ location / {
+ proxy_pass http://nodejs:3000;
+
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection 'upgrade';
+ proxy_set_header Host $host;
+ proxy_cache_bypass $http_upgrade;
+ }
+}
\ No newline at end of file
diff --git a/database_dump/administrators.json b/database_dump/administrators.json
deleted file mode 100644
index cfaaf6d..0000000
--- a/database_dump/administrators.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "_id" : ObjectId("5de9733dc58a41381415a7b0"),
- "id" : "1575580477024",
- "name" : "Administrator",
- "email" : "admin@support.ru",
- "password" : "$2a$10$l5A1VHuN3a9IXhPq.tK.WOWvrIDZJxu1LKIPOYe6ywlS4dbjIMBu6"
-}
diff --git a/database_dump/buyers.json b/database_dump/buyers.json
deleted file mode 100644
index 6d46fdc..0000000
--- a/database_dump/buyers.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "_id" : ObjectId("5e2c6e4770964b0017bb9009"),
- "bill_id" : "51076c5e-25d7-422e-8f73-9bc3f583679c",
- "email" : "testmail@mail.com",
- "method" : "qiwi",
- "date" : "2020-01-25T19:35:14.025+03:00",
- "amount" : NumberInt(1),
- "data" : [
- "login:pass"
- ]
-}
diff --git a/database_dump/categories.json b/database_dump/categories.json
deleted file mode 100644
index f8ea848..0000000
--- a/database_dump/categories.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "_id" : ObjectId("5df63ed5044ecc18c43e9e91"),
- "title" : "steam",
- "img" : "img-1576420937629.svg",
- "type" : "Steam Type",
- "format" : "Steam Format"
-},
-{
- "_id" : ObjectId("5df64563309e691f2c33d400"),
- "title" : "uplay",
- "img" : "img-1576349954743.svg",
- "type" : "Uplay Type",
- "format" : "Uplay Format"
-},
-{
- "_id" : ObjectId("5e04ea023625771abc75fd14"),
- "title" : "fortnite",
- "img" : "img-1576349989720.svg",
- "type" : "Fortnite Type",
- "format" : "Fortnite Format"
-},
-{
- "_id" : ObjectId("5e11c381471ae208f0b6aae4"),
- "title" : "origin",
- "img" : "img-1576421960189.svg",
- "type" : "Origin Type",
- "format" : "Origin Format"
-}
diff --git a/database_dump/content.json b/database_dump/content.json
deleted file mode 100644
index 0bc1825..0000000
--- a/database_dump/content.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "_id" : ObjectId("5e0b7466fbd02d01c46038a7"),
- "title" : "Store Name",
- "email" : "email@support.ru",
- "info" : "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?",
- "infoBottom" : "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?",
- "howToBuy" : "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?",
- "contacts" : "1. Почта: email@support.ru\r\n2. Telegram: @support.ru",
- "recommendations" : "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?",
- "color" : "#26a69a"
-}
diff --git a/database_dump/info.json b/database_dump/info.json
deleted file mode 100644
index 39792b5..0000000
--- a/database_dump/info.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
- "_id" : ObjectId("5e15dbc415303a1980cd77a0"),
- "title" : "Тестовый товар №1",
- "data" : [
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass"
- ]
-},
-{
- "_id" : ObjectId("5e15dcb4138c3339bcbc3719"),
- "title" : "Тестовый товар №3",
- "data" : [
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass"
- ]
-},
-{
- "_id" : ObjectId("5e175c63b479600ac493ce88"),
- "title" : "Тестовый товар №4",
- "data" : [
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass"
- ]
-},
-{
- "_id" : ObjectId("5e1ccf30d5b894228c86385a"),
- "title" : "Тестовый товар №5",
- "data" : [
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass",
- "login:pass"
- ]
-}
diff --git a/database_dump/items.json b/database_dump/items.json
deleted file mode 100644
index bb7fd51..0000000
--- a/database_dump/items.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "_id" : ObjectId("5e15dbc415303a1980cd77a0"),
- "title" : "Тестовый товар №1",
- "count" : NumberInt(10),
- "price" : NumberInt(1),
- "category" : "steam",
- "description" : "Тестовый товар №1 описание",
- "date" : ISODate("2020-01-08T13:40:20.353+0000")
-},
-{
- "_id" : ObjectId("5e15dcb4138c3339bcbc3719"),
- "title" : "Тестовый товар №3",
- "count" : NumberInt(10),
- "price" : NumberInt(1),
- "category" : "uplay",
- "description" : "Тестовый товар №3 fdsfd",
- "date" : ISODate("2020-01-08T13:44:20.940+0000")
-},
-{
- "_id" : ObjectId("5e175c63b479600ac493ce88"),
- "title" : "Тестовый товар №4",
- "count" : NumberInt(10),
- "price" : NumberInt(1),
- "category" : "fortnite",
- "description" : "Тестовый товар №4 opis",
- "date" : ISODate("2020-01-09T17:01:23.446+0000")
-},
-{
- "_id" : ObjectId("5e1ccf30d5b894228c86385a"),
- "title" : "Тестовый товар №5",
- "count" : NumberInt(10),
- "price" : NumberInt(1),
- "category" : "origin",
- "description" : "sdsdsds",
- "date" : ISODate("2020-01-13T20:12:32.814+0000")
-}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..a3e93d4
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,47 @@
+version: '3'
+services:
+ mongodb:
+ container_name: mongodb
+ image: mongo
+ environment:
+ - MONGO_INITDB_ROOT_USERNAME=root
+ - MONGO_INITDB_ROOT_PASSWORD=secret
+ - MONGO_INITDB_DATABASE=digital_store
+ ports:
+ - '27017'
+ restart: always
+ volumes:
+ - ./data/backup:/backup
+ - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
+ nodejs:
+ container_name: nodejs
+ restart: always
+ build: .
+ ports:
+ - '3000'
+ environment:
+ - DATABASE_NAME=digital_store
+ - DATABASE_URI=mongodb://root:secret@mongodb:27017
+ - EMAIL_LOGIN=
+ - EMAIL_PASSWORD=
+ - HOSTNAME_URI=
+ - QIWI_SECRET_KEY=
+ nginx:
+ image: nginx
+ container_name: nginx
+ restart: unless-stopped
+ ports:
+ - '80:80'
+ - '443:443'
+ volumes:
+ - ./data/nginx:/etc/nginx/conf.d
+ - ./data/certbot/conf:/etc/letsencrypt
+ - ./data/certbot/www:/var/www/certbot
+ command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"'''
+ certbot:
+ image: certbot/certbot
+ restart: unless-stopped
+ volumes:
+ - ./data/certbot/conf:/etc/letsencrypt
+ - ./data/certbot/www:/var/www/certbot
+ entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
diff --git a/ecosystem.config.js b/ecosystem.config.js
deleted file mode 100644
index ef044ab..0000000
--- a/ecosystem.config.js
+++ /dev/null
@@ -1,21 +0,0 @@
-module.exports = {
- apps : [{
- name: 'server',
- script: 'server/server.js',
- autorestart: true,
- watch: true,
- instances: 'max',
- exec_mode: 'cluster',
- env: {
- NODE_ENV: 'development'
- },
- env_production: {
- NODE_ENV: 'production',
- DATABASE_URI: 'mongodb://127.0.0.1:27017',
- DATABASE_NAME: '',
- EMAIL_LOGIN: '',
- EMAIL_PASSWORD: '',
- QIWI_SECRET_KEY: ''
- }
- }],
-};
diff --git a/init-letsencrypt.sh b/init-letsencrypt.sh
new file mode 100644
index 0000000..1e1c29f
--- /dev/null
+++ b/init-letsencrypt.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+if ! [ -x "$(command -v docker-compose)" ]; then
+ echo 'Error: docker-compose is not installed.' >&2
+ exit 1
+fi
+
+domains=(domain.ru www.domain.ru)
+rsa_key_size=4096
+data_path="./data/certbot"
+email="yourmail@mail.ru" # Adding a valid address is strongly recommended
+staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits
+
+if [ -d "$data_path" ]; then
+ read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
+ if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
+ exit
+ fi
+fi
+
+
+if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
+ echo "### Downloading recommended TLS parameters ..."
+ mkdir -p "$data_path/conf"
+ curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
+ curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
+ echo
+fi
+
+echo "### Creating dummy certificate for $domains ..."
+path="/etc/letsencrypt/live/$domains"
+mkdir -p "$data_path/conf/live/$domains"
+docker-compose run --rm --entrypoint "\
+ openssl req -x509 -nodes -newkey rsa:1024 -days 1\
+ -keyout '$path/privkey.pem' \
+ -out '$path/fullchain.pem' \
+ -subj '/CN=localhost'" certbot
+echo
+
+
+echo "### Starting nginx ..."
+docker-compose up --force-recreate -d
+echo
+
+echo "### Deleting dummy certificate for $domains ..."
+docker-compose run --rm --entrypoint "\
+ rm -Rf /etc/letsencrypt/live/$domains && \
+ rm -Rf /etc/letsencrypt/archive/$domains && \
+ rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot
+echo
+
+
+echo "### Requesting Let's Encrypt certificate for $domains ..."
+#Join $domains to -d args
+domain_args=""
+for domain in "${domains[@]}"; do
+ domain_args="$domain_args -d $domain"
+done
+
+# Select appropriate email arg
+case "$email" in
+ "") email_arg="--register-unsafely-without-email" ;;
+ *) email_arg="--email $email" ;;
+esac
+
+# Enable staging mode if needed
+if [ $staging != "0" ]; then staging_arg="--staging"; fi
+
+docker-compose run --rm --entrypoint "\
+ certbot certonly --webroot -w /var/www/certbot \
+ $staging_arg \
+ $email_arg \
+ $domain_args \
+ --rsa-key-size $rsa_key_size \
+ --agree-tos \
+ --force-renewal" certbot
+echo
+
+echo "### Reloading nginx ..."
+docker-compose exec nginx nginx -s reload
diff --git a/mongo-init.js b/mongo-init.js
new file mode 100644
index 0000000..963df57
--- /dev/null
+++ b/mongo-init.js
@@ -0,0 +1,10 @@
+db.createUser({
+ user: 'root',
+ pwd: 'secret',
+ roles: [
+ {
+ role: 'readWrite',
+ db: 'digital_store',
+ },
+ ],
+})
diff --git a/package-lock.json b/package-lock.json
index 0a6c421..37e1ab5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "digital-store",
- "version": "1.0.0",
+ "version": "1.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/server/database.js b/server/database.js
index 39a2e39..2a7eb76 100644
--- a/server/database.js
+++ b/server/database.js
@@ -1,4 +1,4 @@
module.exports = {
- url: process.env.DATABASE_URI,
- name: process.env.DATABASE_NAME
-}
\ No newline at end of file
+ url: process.env.DATABASE_URI,
+ name: process.env.DATABASE_NAME,
+}
diff --git a/server/server.js b/server/server.js
index 48c4703..89f0b37 100644
--- a/server/server.js
+++ b/server/server.js
@@ -1,63 +1,77 @@
if (process.env.NODE_ENV !== 'production') {
- require('dotenv').config();
+ require('dotenv').config()
}
-const MongoClient = require('mongodb').MongoClient;
-const db_config = require('./database');
-const express = require('express');
-const server = express();
-const cors = require('cors');
-const path = require('path');
-const passport = require('passport');
-const flash = require('express-flash');
-const session = require('express-session');
-const methodOverride = require('method-override');
-const multer = require('multer');
-const QiwiBillPaymentsAPI = require('@qiwi/bill-payments-node-js-sdk');
+const MongoClient = require('mongodb').MongoClient
+const db_config = require('./database')
+const express = require('express')
+const server = express()
+const cors = require('cors')
+const path = require('path')
+const passport = require('passport')
+const flash = require('express-flash')
+const session = require('express-session')
+const methodOverride = require('method-override')
+const multer = require('multer')
+const QiwiBillPaymentsAPI = require('@qiwi/bill-payments-node-js-sdk')
-const qiwiApi = new QiwiBillPaymentsAPI(process.env.QIWI_SECRET_KEY);
+const qiwiApi = new QiwiBillPaymentsAPI(process.env.QIWI_SECRET_KEY)
-MongoClient.connect(db_config.url, {useNewUrlParser: true, useUnifiedTopology: true})
- .then((client) => {
- const db = client.db(db_config.name);
- const storage = multer.diskStorage({
- destination: 'dist/public/img/service-icons',
- filename: function(req, file, cb) {
- cb(null, `${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`);
- }
- });
-
- // Config
- server.use(cors());
- server.use(express.static(`${__dirname}/../dist/public`));
- server.use(multer({storage: storage}).single('img'));
- server.use(express.urlencoded({ extended: false }));
- server.use(express.json());
- server.use(flash());
- server.use(session({secret: require('crypto-random-string')({length: 64, type: 'base64'}), resave: true,saveUninitialized: false}));
- server.use(passport.initialize());
- server.use(passport.session());
- server.use(methodOverride('_method'));
- server.set('view engine', 'pug');
- server.set('views', `${__dirname}/../dist/views`);
-
- // Routes
- require('./routes/main')(server, db); // маршруты сайта
- require('./routes/admin')(server, db); // маршрут с формой авторизации админа
- require('./routes/control-panel')(server, db); // маршруты с панелью управления
- require('./routes/api')(server, db); // отдача данных клиенту
-
- // Payments
- require('./routes/success')(server, db, qiwiApi);
- require('./routes/payments/qiwi.payment')(server, db, qiwiApi);
-
- // 404 Redirect
- server.use((req, res) => res.status(404).render('main/404'));
+MongoClient.connect(db_config.url, {
+ useNewUrlParser: true,
+ useUnifiedTopology: true,
+})
+ .then((client) => {
+ const db = client.db(db_config.name)
+ const storage = multer.diskStorage({
+ destination: 'dist/public/img/service-icons',
+ filename: function (req, file, cb) {
+ cb(
+ null,
+ `${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`
+ )
+ },
})
- .catch((error) => {
- server.get('*', (req, res) => {
- res.send('Произошла ошибка при запуске. Используйте команду "pm2 log" для того чтобы узнать ошибку.');
- console.log(error);
- });
+
+ // Config
+ server.use(cors())
+ server.use(express.static(`${__dirname}/../dist/public`))
+ server.use(multer({ storage: storage }).single('img'))
+ server.use(express.urlencoded({ extended: false }))
+ server.use(express.json())
+ server.use(flash())
+ server.use(
+ session({
+ secret: require('crypto-random-string')({ length: 64, type: 'base64' }),
+ resave: true,
+ saveUninitialized: false,
+ })
+ )
+ server.use(passport.initialize())
+ server.use(passport.session())
+ server.use(methodOverride('_method'))
+ server.set('view engine', 'pug')
+ server.set('views', `${__dirname}/../dist/views`)
+
+ // Routes
+ require('./routes/main')(server, db) // маршруты сайта
+ require('./routes/admin')(server, db) // маршрут с формой авторизации админа
+ require('./routes/control-panel')(server, db) // маршруты с панелью управления
+ require('./routes/api')(server, db) // отдача данных клиенту
+
+ // Payments
+ require('./routes/success')(server, db, qiwiApi)
+ require('./routes/payments/qiwi.payment')(server, db, qiwiApi)
+
+ // 404 Redirect
+ server.use((req, res) => res.status(404).render('main/404'))
+ })
+ .catch((error) => {
+ server.get('*', (req, res) => {
+ res.send('Произошла ошибка.')
+ console.log(error)
})
- .finally(server.listen(process.env.PORT || 3000, console.log('App is running...')));
+ })
+ .finally(
+ server.listen(process.env.PORT || 3000, console.log('App is running...'))
+ )