# Установка exchanger-api

{% hint style="info" %}
Установка Rest-API на собственные сервера доступна только при покупке полной лицензии ПО BoxExchanger
{% endhint %}

{% tabs %}
{% tab title="Docker" %}

#### Prepare dependencies

```bash
apt update
apt upgrade -y
apt install -y nano sudo curl wget
```

#### 1. Install Docker and Docker Compose

```bash
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
```

#### 2. Create runner user and add it to sudoers <a href="#user-content-2-create-runner-user-and-add-it-to-sudoers" id="user-content-2-create-runner-user-and-add-it-to-sudoers"></a>

```bash
sudo adduser --disabled-password --gecos "" runner
sudo usermod -aG sudo runner
```

#### 3. Create docker group and add user to it <a href="#user-content-3-create-docker-group-and-add-user-to-it" id="user-content-3-create-docker-group-and-add-user-to-it"></a>

```bash
sudo usermod -aG docker runner
```

#### 4. Switch to a 'runner' user <a href="#user-content-4-switch-to-a-runner-user" id="user-content-4-switch-to-a-runner-user"></a>

```bash
sudo su runner
```

```bash
newgrp docker
```

#### 5. Create docker network <a href="#user-content-10-create-docker-network" id="user-content-10-create-docker-network"></a>

```bash
docker network create --subnet 10.1.0.0/24 exchanger-net
docker network create --subnet 10.255.255.0/24 control-net
```

#### 6. Docker login

* Create a Personal Access Token in GitLab
* Make sure to tick the `read_registry` permission scope
* Create a reminder to update the PAT after expiration date, as once it expires you lose access for updates.\
  <https://git.boxexchanger.net/-/profile/personal_access_tokens>
* Login to docker read\_registry

  <pre class="language-bash"><code class="lang-bash">docker login rg.boxexchanger.net

  <strong># Username: your_gitlab_username
  </strong># Password: your_gitlab_pat
  </code></pre>

#### 7. Create required folders <a href="#user-content-7-create-required-folders" id="user-content-7-create-required-folders"></a>

```bash
mkdir -p /home/runner/api_server
mkdir -p /home/runner/api_server/config
mkdir -p /home/runner/api_server/public
mkdir -p /home/runner/api_server/pluginsExternal
cd /home/runner/api_server
```

#### 8. Create your configurations (.env) <a href="#user-content-8-copy-the-files-to-homerunnerapi_server" id="user-content-8-copy-the-files-to-homerunnerapi_server"></a>

```bash
nano /home/runner/api_server/.env
```

{% hint style="warning" %}
Below example configuration .env\
&#x20; Insert your variables `PROJECT_NAME`, `API_URL`  and `VCS_NAMESPACE`
{% endhint %}

<pre class="language-bash"><code class="lang-bash">PROJECT_NAME="<a data-footnote-ref href="#user-content-fn-1">Project_name</a>"
API_URL="https://<a data-footnote-ref href="#user-content-fn-1">www.domain.name</a>/service/"
REDIS_URI="redis://exchanger-api-redis:6379/0"
UPLOADER_URL="http://exchanger-uploader-service:3013/"
VCS_NAMESPACE="<a data-footnote-ref href="#user-content-fn-2">bx4/project-name</a>"
</code></pre>

#### 8.1  Create your docs configuration (.env.docs.config.js) <a href="#user-content-8-copy-the-files-to-homerunnerapi_server" id="user-content-8-copy-the-files-to-homerunnerapi_server"></a>

```bash
nano /home/runner/api_server/.env.docs.config.js
```

<pre class="language-javascript"><code class="lang-javascript">const e = {
    PROJECT_NAME:"<a data-footnote-ref href="#user-content-fn-3">Project_name</a>",
    BASE_URL:"https://<a data-footnote-ref href="#user-content-fn-2">www.domain.name</a>",
    API_PATH:"/service/api/v1",
    API_URL:"https://<a data-footnote-ref href="#user-content-fn-2">www.domain.name</a>/service/api/v1/",
};
export {e as config};
</code></pre>

#### 8.2 Create your lifecycle manager secret (.env.lm)&#x20;

{% hint style="warning" %}
For security reason **generate new strong secret** `DOCKER_LIFECYCLE_MANAGER_KEY` \
for generate use `openssl rand -hex 32`
{% endhint %}

```bash
nano /home/runner/api_server/.env.lm
```

```bash
DOCKER_LIFECYCLE_MANAGER_URL=http://exchanger-lifecycle-manager:8000/
DOCKER_LIFECYCLE_MANAGER_KEY=932a4dd476573c9b7d39d932d1b2bc04f01b0b028d7e2475567eb849ea3002db
```

#### 9. Create nginx configuration for api <a href="#user-content-8-copy-the-files-to-homerunnerapi_server" id="user-content-8-copy-the-files-to-homerunnerapi_server"></a>

Create /home/runner/api\_server/nginx\_api.conf file with following contents&#x20;

<pre class="language-bash"><code class="lang-bash"><strong>nano /home/runner/api_server/nginx_api.conf
</strong></code></pre>

```nginx
map $http_upgrade $connection_upgrade {
    default upgrade;
    ""      close;
}

log_format cf_custom '$remote_addr - $remote_user [$time_local] "$request" '
                     '$status $body_bytes_sent "$http_referer" '
                     '"$http_user_agent" "$http_x_forwarded_for" '
                     '"$http_cf_ray" "$http_cf_connecting_ip"';
server {
    listen 3000;
    server_tokens off;
    server_name default;

    location /service/ {
        proxy_pass http://exchanger-api-http:3010/;
        proxy_http_version 1.1;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }
    location /service/api-docs/ {
        add_header Cache-Control "public, max-age=900";
        proxy_pass http://exchanger-docs:8080/;
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }
    location /service/fs {
        add_header Cache-Control "public, max-age=900";
        alias /public;
    }
    location /ws/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_pass http://exchanger-api-websocket:3011/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_redirect    off;
    }

    access_log /dev/stdout cf_custom;
    error_log /dev/stderr error;
    sendfile off;
    client_max_body_size 100m;
}
```

#### 10.  Create your docker-compose.yml <a href="#user-content-8-copy-the-files-to-homerunnerapi_server" id="user-content-8-copy-the-files-to-homerunnerapi_server"></a>

```bash
nano /home/runner/api_server/docker-compose.yml
```

```yaml
x-logging:
  &default-logging
  driver: "json-file"
  options:
    max-file: "1"
    max-size: "500m"

services:

  exchanger-api-http:
    image: rg.boxexchanger.net/${VCS_NAMESPACE}/exchanger-api:${API_BRANCH:-master}
    container_name: exchanger-api-http
    entrypoint: bash ./entry.sh API
    restart: unless-stopped
    read_only: true
    depends_on:
      - exchanger-uploader-service
      - exchanger-api-mongodb
      - redis
    env_file:
      - .env.mongo.user
      - .env.lm
      - .env
    volumes:
      - ./config:/app/config
      - ./public:/app/public
      - ./pluginsExternal:/app/pluginsExternal
      - type: tmpfs
        target: /home/node/.pm2/
        tmpfs:
          size: "10000000"
          mode: 0777
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.101
      control-net:
        ipv4_address: 10.255.255.25
  exchanger-lifecycle-manager:
    image: rg.boxexchanger.net/boxexchanger-utils/exchanger-lifecycle-manager:main@sha256:5315489b02d27f319467f3161b64f69d76f4ba8aaf951dd921e92b2613671502
    container_name: exchanger-lifecycle-manager
    restart: unless-stopped
    read_only: true
    env_file:
      - .env.lm
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      control-net:
        ipv4_address: 10.255.255.24

  exchanger-api-websocket:
    image: rg.boxexchanger.net/${VCS_NAMESPACE}/exchanger-api:${API_BRANCH:-master}
    container_name: exchanger-api-websocket
    entrypoint: bash ./entry.sh WEBSOCKET
    restart: unless-stopped
    read_only: true
    depends_on:
      - exchanger-api-http
    env_file:
      - .env.mongo.user
      - .env
    volumes:
      - ./config:/app/config:ro
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.107
  exchanger-api-parser-rate:
    image: rg.boxexchanger.net/${VCS_NAMESPACE}/exchanger-api:${API_BRANCH:-master}
    container_name: exchanger-api-parser-rate
    entrypoint: bash ./entry.sh PARSER_RATE
    restart: unless-stopped
    read_only: true
    depends_on:
      - exchanger-api-http
    env_file:
      - .env.mongo.user
      - .env
    volumes:
      - ./config:/app/config:ro
      - ./public:/app/public
      - ./pluginsExternal:/app/pluginsExternal:ro
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.108
  exchanger-api-cron:
    image: rg.boxexchanger.net/${VCS_NAMESPACE}/exchanger-api:${API_BRANCH:-master}
    container_name: exchanger-api-cron
    entrypoint: bash ./entry.sh CRON
    restart: unless-stopped
    read_only: true
    depends_on:
      - exchanger-api-http
    env_file:
      - .env.mongo.user
      - .env
    volumes:
      - ./config:/app/config
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.109
  exchanger-api-payout:
    image: rg.boxexchanger.net/${VCS_NAMESPACE}/exchanger-api:${API_BRANCH:-master}
    container_name: exchanger-api-payout
    entrypoint: bash ./entry.sh PAYOUT
    restart: unless-stopped
    read_only: true
    depends_on:
      - exchanger-api-http
    env_file:
      - .env.mongo.user
      - .env
    volumes:
      - ./config:/app/config:ro
      - ./public:/app/public
      - ./pluginsExternal:/app/pluginsExternal:ro
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.110
  exchanger-api-aml:
    image: rg.boxexchanger.net/${VCS_NAMESPACE}/exchanger-api:${API_BRANCH:-master}
    container_name: exchanger-api-aml
    entrypoint: bash ./entry.sh AML
    restart: unless-stopped
    read_only: true
    depends_on:
      - exchanger-api-http
    env_file:
      - .env.mongo.user
      - .env
    volumes:
      - ./config:/app/config:ro
      - ./public:/app/public
      - ./pluginsExternal:/app/pluginsExternal:ro
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.111
  exchanger-api-module:
    image: rg.boxexchanger.net/${VCS_NAMESPACE}/exchanger-api:${API_BRANCH:-master}
    container_name: exchanger-api-module
    entrypoint: bash ./entry.sh MODULE
    restart: unless-stopped
    read_only: true
    depends_on:
      - exchanger-api-http
    env_file:
      - .env.mongo.user
      - .env
    volumes:
      - ./config:/app/config:ro
      - ./public:/app/public
      - ./pluginsExternal:/app/pluginsExternal:ro
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.112
  exchanger-api-notification:
    image: rg.boxexchanger.net/${VCS_NAMESPACE}/exchanger-api:${API_BRANCH:-master}
    container_name: exchanger-api-notification
    entrypoint: bash ./entry.sh NOTIFICATION
    restart: unless-stopped
    read_only: true
    depends_on:
      - exchanger-api-http
    env_file:
      - .env.mongo.user
      - .env
    volumes:
      - ./config:/app/config:ro
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.113

  exchanger-uploader-service:
    image: rg.boxexchanger.net/boxexchanger-utils/exchanger-uploader-service:master
    container_name: exchanger-uploader-service
    restart: unless-stopped
    read_only: true
    volumes:
      - ./public:/public
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.104
  exchanger-email-service:
    image: rg.boxexchanger.net/boxexchanger-utils/exchanger-email-service:master
    container_name: exchanger-email-service
    restart: unless-stopped
    read_only: true
    volumes:
      - ./config:/app/config:ro
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.106

  exchanger-docs:
      image: rg.boxexchanger.net/boxexchanger-utils/restapi-docs-ui:master
      container_name: exchanger-docs
      restart: unless-stopped
      read_only: true
      volumes:
        - ./.env.docs.config.js:/usr/share/nginx/html/dist/_nuxt/_CONFIG.js:ro
        - type: tmpfs
          target: /tmp/
          tmpfs:
            size: "1000000"
      logging: *default-logging
      networks:
        exchanger-net:
          ipv4_address: 10.1.0.105
  exchanger-api-mongodb:
    image: mongo:6
    container_name: exchanger-api-mongodb
    restart: unless-stopped
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.102
    volumes:
      - mongo_db:/data/db
      - /backups:/backups
      - ./init_db.js:/docker-entrypoint-initdb.d/init.js
    logging: *default-logging
    ports:
      - 127.0.0.1:27017:27017
    env_file:
      - .env.mongo.root

  redis:
    image: redis:7
    container_name: exchanger-api-redis
    restart: unless-stopped
    read_only: true
    volumes:
      - redis_data:/data
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.103

  nginx-api:
    image: nginxinc/nginx-unprivileged:1.27-alpine
    container_name: nginx-api
    restart: unless-stopped
    read_only: true
    depends_on:
      - exchanger-api-http
    volumes:
      - ./nginx/:/etc/nginx/conf.d:ro
      - ./public:/public:ro
      - type: tmpfs
        target: /docker-entrypoint.d/
        tmpfs:
          size: "100000"
      - type: tmpfs
        target: /tmp/
        tmpfs:
          size: "10000000"
    ports:
      - "3000:3000"
    logging: *default-logging
    networks:
      exchanger-net:
        ipv4_address: 10.1.0.100
volumes:
  mongo_db:
  redis_data:

networks:
  exchanger-net:
    external: true
  control-net:
    external: true
```

#### 11. Execute credential mongo generation script <a href="#user-content-11-execute-credential-generation-script" id="user-content-11-execute-credential-generation-script"></a>

<pre class="language-bash"><code class="lang-bash"><strong>cd /home/runner/api_server
</strong><strong>wget "https://git.boxexchanger.net/-/snippets/2/raw/main/init_db.js"
</strong><strong>wget "https://git.boxexchanger.net/-/snippets/2/raw/main/generate_credentials.sh"
</strong>chmod +x generate_credentials.sh
./generate_credentials.sh
</code></pre>

#### 12. Initialize MongoDB <a href="#user-content-12-initialize-mongodb-and-create-database" id="user-content-12-initialize-mongodb-and-create-database"></a>

```bash
docker compose up -d exchanger-api-mongodb
```

#### 13.  Start API server

```bash
docker compose up -d
```

#### 14. Get and remove initial admin credentials:

```bash
docker exec exchanger-api cat access.txt
```

* remove access.txt

  ```bash
  docker exec exchanger-api rm access.txt
  ```

{% endtab %}

{% tab title="Sources" %}

## 1. Подготовка окружения

После установки сервера к нему необходимо подключиться по SSH и настроить окружение

#### Установить пакеты сервера (nano git curl)

<pre class="language-bash"><code class="lang-bash">apt update
apt upgrade -y
<strong>apt-get install -y curl git nano wget sudo
</strong></code></pre>

#### Установить NodeJS:

{% content-ref url="ustanovka-nodejs" %}
[ustanovka-nodejs](https://manual.boxexchanger.net/dlya-razrabotchikov/ustanovka-proekta/ustanovka-po/ustanovka-nodejs)
{% endcontent-ref %}

#### Установить MongoDB:

{% content-ref url="ustanovka-mongodb" %}
[ustanovka-mongodb](https://manual.boxexchanger.net/dlya-razrabotchikov/ustanovka-proekta/ustanovka-po/ustanovka-mongodb)
{% endcontent-ref %}

#### Установить Redis

```bash
apt-get install -y redis-server
```

#### Установить пакеты g ++ build-essential imagemagick graphicsmagick

```bash
apt-get install -y g++ build-essential imagemagick graphicsmagick
```

#### Установить PM2

```bash
npm i -g pm2
```

## 2. Скачивание ПО на сервер

#### Создаем SSH-ключ

Документация с github.com [Создание нового ключа SSH](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent)

Учебник по Linux

```bash
ssh-keygen -t rsa -b 4096
```

\> Enter x3

```bash
eval "$(ssh-agent -s)"
```

```bash
ssh-add ~/.ssh/id_rsa
```

Получить свой ключ:

```bash
cat ~/.ssh/id_rsa.pub
```

Скопируйте результат и установите данный ключ в своей учётной записи [git.boxexchanger.net](https://git.boxexchanger.net/-/profile/keys)&#x20;

{% content-ref url="../../dostup-k-iskhodnomu-kodu" %}
[dostup-k-iskhodnomu-kodu](https://manual.boxexchanger.net/dlya-razrabotchikov/dostup-k-iskhodnomu-kodu)
{% endcontent-ref %}

#### Клонируем репозитории

```bash
cd /var/www
```

**NAME\_SPACE** - вы можете узнать в вашем [личном кабинете](https://git.boxexchanger.net/) полностью ссылку на клонирование репозитория.

```bash
git clone git@ssh.boxexchanger.net:bx4/NAME_SPACE/exchanger-api.git
```

## 3. Конфигурирование и запуск Rest-API

```bash
cd /var/www/exchanger-api/
```

```bash
npm ci
```

Настроить конфигурацию вашего проекта

```bash
nano .env
```

{% hint style="warning" %}
**Внимание!** Вам необходимо указать свое название проекта доменное имя и доступы к базе данных: **PROJECT\_NAME, API\_URL, MONGO\_URI**
{% endhint %}

```bash
PROJECT_NAME="_PROJECT_NAME"
API_URL="https://www.domain.name/"
REDIS_URI="redis://127.0.0.1:6379/0"
MONGO_URI="mongodb://USERNAME:PASSWORD@127.0.0.1:27017/DB_NAME"
MONGO_DEBUG=false
SEND_LOGS=true
HTTP_PORT=3010
HTTP_DEBUG=false
WEBSOCKET_PORT=3011
LICENSE_PORT=3012
APP_DEBUG=false
```

Применить конфигурацию:

```bash
npm run configure
```

```bash
npm run seed
```

Найдите строчку !! SAVE !!!, \*\*\*\*\*@admin.ex \*\*\*\*\* Сохраните данные это ваш логин и пароль от админ панели!

```bash
npm run migrate
```

Собрать документацию API:

<pre class="language-bash"><code class="lang-bash">npm install --no-save api-bxdocs
<strong>npm run build-api
</strong></code></pre>

```bash
npm run start
```

Проверить api:

```bash
curl http://localhost:3010
```

ответ должен содержать:

`this is "service path" for project ...`
{% endtab %}
{% endtabs %}

[^1]: Change it!

[^2]: Change it

[^3]: Change it.
