# Установка 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="/pages/Po9scEwrPiPdFfdk7zAR" %}
[Установка NodeJs](/dlya-razrabotchikov/ustanovka-proekta/ustanovka-po/ustanovka-nodejs.md)
{% endcontent-ref %}

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

{% content-ref url="/pages/dD7QFntdw0Ms1qClzrfw" %}
[Установка MongoDB](/dlya-razrabotchikov/ustanovka-proekta/ustanovka-po/ustanovka-mongodb.md)
{% 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="/pages/lxzi4kPvGuwQJojMrdnE" %}
[Доступ к исходному коду](/dlya-razrabotchikov/dostup-k-iskhodnomu-kodu.md)
{% 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://manual.boxexchanger.net/dlya-razrabotchikov/ustanovka-proekta/ustanovka-po/ustanovka-exchanger-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
