Было бы очень полезно установить отдельно сетевой сервер TTN, сервер приложений и сервер присоединения. В настоящее время в руководствах я нашел только инструкции по установке ttn-lw-stack all-in-one, но нет возможности устанавливать каждый сервер отдельно, если вы хотите, чтобы они работали вместе в разных средах.
...
Это отличная возможность, позволяющая использовать гибкие методы развертывания. Вы можете установить все 3 сервера (NS, AS и JS) на шлюз, или вы можете выбрать другой сервер с JS и оставить только NS и AS на шлюзе, чтобы обеспечить централизованное и удаленное управление несколькими шлюзами, и т. Д. на.
...
Прямо сейчас я вижу только способ установки ttn-lw-stack, который включает все 3 сервера (NS, AS и JS).
...
Я хотел бы видеть инструкции по отдельной установке NS, AS и JS вместо того, чтобы иметь их все в одной установке / пакете.
...
Добавьте его в руководство по началу работы.
...
На данный момент нет, я не уверен, что это уже частично реализовано, и, вероятно, кто-то знает, как сделать это более эффективно, чем я.
...
Спасибо за предложение @zamashal
Действительно, «Начало работы» в настоящее время относится к единому процессу, но, как вы могли заметить, вы можете запускать компоненты по отдельности. Видеть;
$ ttn-lw-stack start --help
Start The Things Stack
Usage:
ttn-lw-stack start [is|gs|ns|as|js|console|gcs|dtc|qrg|all]... [flags]
Создавать службы для каждого компонента не очень сложно, если эти службы являются частью одного кластера и подсети.
Я сейчас рассматриваю эту проблему, чтобы получить инструкции о том, как это сделать;
@johanstokking Большое спасибо за ваш ответ и добавление проблемы в очередь. А пока мне интересно, можете ли вы мне с этим помочь. Я запустил сервер присоединения один с помощью следующей команды:
ttn-lw-stack start js --cluster.network-server "ns_ip_address" --cluster.application-server "as_ip_address"
Я не могу понять, на каком порту сервер соединения получает Join_Req и будет ли он автоматически отправлять Join_Ans на указанный сетевой сервер?
Спасибо еще раз!
@zamashal на самом деле JS - это сервер, а NS и AS - клиенты. Поэтому настройте адрес кластера JS в NS и AS. Это заставляет их работать в одном кластере, хотя они являются отдельными компонентами. Обратите внимание, что здесь используется проверка подлинности кластера, которая предназначена для компонентов, доверяющих друг другу в одном кластере. Если вы развертываете GS, NS и AS на периферии, а JS в облаке, это, вероятно, не так.
В этом случае вы должны использовать взаимодействие через внутренние интерфейсы LoRaWAN, которые также поддерживаются. Это позволяет NS связываться с вашим JS через аутентификацию клиента TLS.
Это состоит из двух частей: настройка NS для использования вашего JS и настройка JS с конфигурацией interop
(см. --help
). К сожалению, это еще не полностью задокументировано.
Еще раз спасибо взаимодействие с Semtech Join Server . Однако я пытаюсь использовать сам сервер соединения TTN Stack, а не что-то внешнее, например, Semtech или другие. Мне все еще нужно вводить конфигурацию для configure.yml
и example/js.yml
? Если да, то как тогда это будет выглядеть?
Я уже настроил свой NS для работы с внешним JS (также известным как TTN Stack JS), но используя порт 8886
(Interop / tls) присоединяемого сервера для отправки Join_Req, в соединении отказывают, хотя JS, похоже, прослушивает этот порт.
Спасибо!
@zamashal Вот примерно то, что нужно сделать;
Смотрите флаги:
--interop.listen-tls string Address for the interop server to listen on (default ":8886")
--interop.sender-client-ca.blob.bucket string Bucket to use
--interop.sender-client-ca.blob.path string Path to use
--interop.sender-client-ca.directory string OS filesystem directory, which contains sender client CA configuration
--interop.sender-client-ca.source string Source of the sender client CA configuration (static, directory, url, blob)
--interop.sender-client-ca.url string URL, which contains sender client CA configuration
У Interop есть собственный выделенный слушатель, который использует аутентификацию клиента TLS. Вы можете использовать тот же общедоступный IP-адрес, что и для gRPC, и использовать выделенный порт взаимодействия (по умолчанию 8886).
Вам нужен частный ЦС, который выдает сертификаты клиентов. Они используются NS на краю. Вы можете настроить доверенные клиентские ЦС на сервере присоединения, и это для каждого NetID. Вы всегда можете использовать NetID 000000
и 000001
в своей частной сети или присоединиться к LoRa Alliance и получить его сами.
Установите для interop.sender-client-ca.source
значение directory
и введите config.yml
например:
# Experimentation
000000: ca-000000.pem
# The Things Network Foundation
#000013: ca-000013.pem
Ваш частный ЦС входит в ca-000000.pem
. Вы можете добавить CA TTN для TTN NetID, как в примере, просто чтобы показать вам, как это работает.
Это похоже на документированное , но на самом деле вам нужна локальная конфигурация JS. Это было бы следующим образом:
fqdn: 'thethings.example'
port: 8886
protocol: 'BI1.0'
tls:
root-ca: 'path/to/clientca.pem'
certificate: 'path/to/clientcert.pem'
key: 'path/to/clientkey.pem'
Здесь thethings.example
- это полное доменное имя вашего сервера присоединения, а 8886 - порт того listen-tls
который вы настроили в JS-взаимодействии.
Кроме того, root-ca
является (в отличие от того, что говорится в примере) корневым центром сертификации _серверного сертификата_. Это может быть тот же СА. Вы также можете не указывать его, если используете коммерческий (или Let's Encrypt) сертификат сервера, которому NS уже доверяет.
Включите журналы отладки с обеих сторон ( log.level=debug
), и вы должны увидеть, что что-то работает, или проследить, почему что-то не работает. Удачи!
Кроме того, если вы сделаете эту работу, не стесняйтесь подавать запрос на перенос для документирования этого. Возможно, потребуется руководство, но справочная страница также нуждается в некоторой любви.
@johanstokking , я буду работать над этим и, надеюсь, как только я это сделаю пул-реквест , чтобы обновить руководство. Не могу отблагодарить вас за всю вашу помощь!
Привет, @johanstokking, надеюсь, у тебя все хорошо. Я хотел бы сообщить вам о моем прогрессе. К сожалению, я исправлял множество ошибок, чтобы это работало, и я поделюсь с вами здесь последними ошибками, с которыми я столкнулся. После настройки взаимодействия и настройки моего сетевого сервера для отправки запросов на присоединение к серверу присоединения через порт по умолчанию 8886, я продолжаю получать следующую ошибку в журнале сетевого сервера:
error="join-request to join-server error: http post error: Post http://js-server_ip:8886: dial tcp js-server_ip:8886: connect: connection refused"
Если я настраиваю свой сетевой сервер для отправки запросов на присоединение к порту 1884 сервера gRPC, вместо этого я получаю следующую ошибку в журнале сетевого сервера:
level=error msg="uplink: processing uplink frame error" ctx_id=f046310d-e528-4dd2-9dcb-6d5c8232a799 error="join-request to join-server error: http post error: Post http://js-server_ip:1884: net/http: HTTP/1.x transport connection broken: malformed HTTP response \"\\x00\\x00\\f\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00@\\x00\\x00\\x03\\x00\\x00\\xff\\xff\""
в сочетании со следующей ошибкой из журнала стека ttn:
stack_1 | WARN grpc: Server.Serve failed to create ServerTransport: connection error: desc = "transport: http2Server.HandleStreams received bogus greeting from client: \"POST / HTTP/1.1\\r\\nHost: 1\"" namespace=grpc
Я надеюсь, что вы или кто-либо другой можете помочь мне понять, как устранить эти ошибки, и узнать, что может вызвать такие ошибки.
Еще раз спасибо за вашу постоянную поддержку!
Сервер присоединения доступен только через https.
Также похоже, что NS не может разрешить js-server_ip
через DNS.
Спасибо, @johanstokking! Так что да, оказывается, я не сопоставил порт 8886 своему хосту в docker-compose.yml. Теперь проблема, с которой я столкнулся, - это ошибка рукопожатия TLS:
tls: failed to verify client's certificate: x509: certificate signed by unknown authority
Во-первых, я использовал флаг --tls.insecure-skip-verify
но он по-прежнему настаивал на проверке сертификата и выдавал мне ту же ошибку. Я думаю, проблема в том, что мне нужно доверять центру сертификации в моем контейнере докеров. Я открыл оболочку в стеке, и она выдавала мне ошибку Permission denied
всякий раз, когда я пытался скопировать сертификаты в /usr/local/share/ca-certificates/
, чтобы доверять им на машине.
Я думаю, что флаг --tls.insecure-skip-verify
должен был разрешить это, но, возможно, ваша реализация отличается. Моя проблема сейчас в том, что контейнер докеров не дает мне возможности доверять моему самозаверяющему сертификату. Что-то мне там не хватает?
Подписан ли сертификат клиента одним из центров сертификации для SenderID
как определено в конфигурации центра
Это то, что сервер соединения использует для проверки сертификата клиента; не доверие к системе или что-то в этом роде.
Я попытался следовать этому, но это не полностью соответствует инструкциям на веб-сайте .
В моем config.yml есть следующее:
000000: ca-000000.pem
join-servers:
- file: './example/js.yml'
join-euis:
- 'abcd000000000000/16'
а затем я поместил это в свой js.yml:
fqdn: 'thethings.example'
port: 8886
protocol: 'BI1.0'
tls:
root-ca: 'path/to/clientca.pem'
certificate: 'path/to/clientcert.pem'
key: 'path/to/clientkey.pem'
Клиентские ЦС отправителя еще не задокументированы, мы сделаем это в рамках закрытия или замены этой проблемы. См. (Здесь) [ https://github.com/TheThingsNetwork/lorawan-stack/issues/1818#issuecomment -575534345]. Это специальный файл, и у него есть собственная настройка для ссылки на файл:
--interop.sender-client-ca.blob.bucket string Bucket to use
--interop.sender-client-ca.blob.path string Path to use
--interop.sender-client-ca.directory string OS filesystem directory, which contains sender client CA configuration
--interop.sender-client-ca.source string Source of the sender client CA configuration (static, directory, url, blob)
--interop.sender-client-ca.url string URL, which contains sender client CA configuration
Итак, source
нужно установить в directory
и вы поместите конфигурацию в вышеупомянутом формате в config.yml
в эту папку. Это другой каталог, чем конфигурация взаимодействия.
Спасибо, @johanstokking! Я не понимал, что это должно быть в другом каталоге, я, наконец, решил проблему с сертификатами и теперь обрабатываю эту ошибку из журнала отладки ttn-stack (я намеренно закрыл ключи, но они были правильными):
stack_1 | INFO Join not accepted dev_eui=0000000000000000 error=error:pkg/redis:not_found (entity not found) join_eui=0000000000000000 method=POST namespace=joinserver/interop remote_addr=gateway_ip:49426 request_id=01E1D3PZ63CQ7VNCE5JE8SDC3J url=/
stack_1 | INFO Request handled duration=2.948762ms error=error:pkg/interop:join_req (join-request failed) error_cause=error:pkg/redis:not_found (entity not found) method=POST namespace=interop remote_addr=gateway_ip:49426 request_id=01E1D3PZ63CQ7VNCE5JE8SDC3J status=400 url=/
Обратите внимание, что gateway_ip также находится там, где находятся NS и AS.
То же самое я вижу в журнале отладки NS:
time="2020-02-18T16:36:52-05:00" level=error msg="uplink: processing uplink frame error" ctx_id=ef20804f-13a8-4f7f-b90e-ce279c1e11ea error="join-request to join-server error: response error, code: JoinReqFailed, description: error:pkg/redis:not_found (entity not found)"
Судя по тому, что я могу прочитать, ошибка, похоже, связана с неправильной конфигурацией моего компонента redis файла docker-compose. Я еще раз просмотрел руководство по настройке, чтобы убедиться, что все совпадает. В моей конфигурации было следующее:
volumes:
- ${DEV_DATA_DIR:-.env/data}/redis:/data
Итак, я пошел дальше и изменил его на это:
volumes:
- './data/redis:/data'
Затем я начал видеть следующую ошибку, которая даже не позволяет мне запустить стек:
stack_1 | error:cmd/internal/shared:initialize_identity_server (could not initialize Identity Server)
stack_1 | --- error:pkg/identityserver:db_needs_migration (the database needs to be migrated)
stack_1 | --- pq: database "ttn_lorawan" does not exist
Я не был уверен, было ли это изменение вообще необходимо, в разделе ./data/redis/
я вижу только один файл `` appendonly.aof``, поэтому мне кажется, что я чего-то упускаю ..
Я не был уверен, было ли это изменение вообще необходимо, в разделе
./data/redis/
я вижу только один файл `` appendonly.aof``, поэтому мне кажется, что я чего-то упускаю ..
Нет, на самом деле это нормально для Redis.
Похоже, ваше устройство не зарегистрировано на сервере присоединения?
О, наверное, поэтому. Все, что я сделал, это использовал флаг --js.join-eui-prefix
но, похоже, этого недостаточно. Я застрял на другом вопросе, который пытался игнорировать: выпуск 1942 г.
Могу ли я зарегистрировать устройство, вручную добавив строки в базу данных Redis? Если да, то какой формат? Это может помочь мне пока что не обращать внимания на другую проблему ...
Мне удалось получить доступ к приборной панели по другой проблеме и зарегистрировать устройство на приборной панели. Теперь я вижу сообщение об ошибке sender unknown
которое, как мне кажется, жалуется на то, что шлюз не распознается. Я попытался добавить шлюз с консоли, но он все еще говорит Disconnected
. Я попытался ввести адрес gateway_ip и server_ip, но оба пока не имели никакого значения.
Неизвестный отправитель, вероятно, означает, что NetID конечного устройства не установлен на NetID вашего сетевого сервера. Оба должны быть установлены в 000000
.
Вы можете установить NetID конечного устройства через интерфейс командной строки с помощью ttn-lw-cli end-device set <app-id> <dev-id> --net-id=000000
Мой ttn-lw-cli
ведет себя странно, я могу запустить команду входа только с параметрами по умолчанию, и если я укажу что-нибудь в файле конфигурации или центре сертификации, я просто получу permission denied
. Я попробовал несколько способов обойти разрешения, изменив chmod и chown. Я продолжаю получать permission denied
. Если я запустил конфигурации по умолчанию, набрав только ttn-lw-cli login
я получу:
Post https://localhost:8885/oauth/token: x509: certificate signed by unknown authority
Хотя docker-compose up работает нормально, без проблем с сертификатом или каких-либо других ошибок. Любая идея, что мне может не хватать, что, вероятно, вызывает отказ в разрешениях?
Спасибо!
Можете ли вы опубликовать конфигурацию вашего сервера и интерфейса командной строки и что именно вы пытаетесь сделать?
Я просто пытался сначала войти в систему с помощью команды sudo ttn-lw-cli login
, вот моя конфигурация:
# sudo ttn-lw-cli config
--allow-unknown-hosts="false"
--application-server-enabled="true"
--application-server-grpc-address="localhost:8884"
--ca=""
--config="/etc/ttn-cli/.ttn-lw-cli.yml,/root/snap/ttn-lw-stack/149/.ttn-lw-cli.yml,/root/snap/ttn-lw-stack/149/.config/.ttn-lw-cli.yml"
--credentials-id=""
--device-claiming-server-grpc-address="localhost:8884"
--device-template-converter-grpc-address="localhost:8884"
--gateway-server-enabled="true"
--gateway-server-grpc-address="localhost:8884"
--identity-server-grpc-address="localhost:8884"
--input-format="json"
--insecure="false"
--join-server-enabled="true"
--join-server-grpc-address="localhost:8884"
--log.level="info"
--network-server-enabled="true"
--network-server-grpc-address="localhost:8884"
--oauth-server-address="https://localhost:8885/oauth"
--output-format="json"
--qr-code-generator-grpc-address="localhost:8884"
Таким образом, запуск по умолчанию дает мне ошибку certificate signed by unknown authority
которой я поделился ранее. Но из-за проблем с сертификатом я попытался добавить следующую опцию: sudo ttn-lw-cli login --ca "path/to/ca.pem"
но это дало мне ошибку с отказом в разрешении.
Я попытался добавить следующий вариант:
sudo ttn-lw-cli login --ca "path/to/ca.pem"
Это хорошо. Вы также можете поместить это в файл конфигурации или среду.
но это дало мне разрешение отклонить ошибку.
На CLI или на сервере? У вас есть логи?
ошибка сервера я думаю? это все, что я вижу:
root<strong i="6">@myserver</strong>:/etc/ttn-cli# sudo ttn-lw-cli login --ca="/etc/ttn-cli/ca.pem" --log.level="debug"
open /etc/ttn-cli/ca.pem: permission denied
Я также пытался дать ему разрешения chmod 777
но все равно получаю ту же ошибку ..
Я наконец смог обойти эту проблему, добавив файл конфигурации в /root/snap/ttn-lw-stack/149/.ttn-lw-cli.yml
!
Я получаю ошибку certificate signed by unknown authority
. Как инструмент ttn-lw-cli
доверяет сертификату? Вот полный журнал:
root<strong i="8">@localhost</strong>:/etc/ttn-stack# sudo ttn-lw-cli login --callback=false --config="/root/snap/ttn-lw-stack/149/.ttn-lw-cli.yml" --log.level="debug" --insecure="true" --allow-unknown-hosts="true" --ca="/root/snap/ttn-lw-stack/149/ca.pem"
WARN Access token expired at 5:17PM
ERROR Please login with the login command
DEBUG ccResolverWrapper: sending update to cc: {[{localhost:1884 <nil> 0 <nil>}] <nil> <nil>}
DEBUG pickfirstBalancer: HandleSubConnStateChange: 0xc00087caa0, {CONNECTING <nil>}
DEBUG pickfirstBalancer: HandleSubConnStateChange: 0xc00087caa0, {READY <nil>}
DEBUG Finished unary call duration=2.376756ms grpc_method=AuthInfo grpc_service=ttn.lorawan.v3.EntityAccess namespace=grpc
INFO Opening your browser on https://localhost/oauth/authorize?client_id=cli&redirect_uri=code&response_type=code
WARN Could not open your browser, you'll have to go there yourself error=fork/exec /usr/bin/xdg-open: permission denied
INFO After logging in and authorizing the CLI, we'll get an access token for future commands.
INFO Please paste the authorization code and press enter
> MF2XI.JX2QFUHNVVWMEYTTRQ3S4DTGPI5VXBYJWVJQ2ZI.OG5C4HQXGMRQ4LVW7ES4IZRNH2L5OJOING2SWOW74LFLQAYDH64Q
ERROR Could not exchange OAuth access token error=Post https://localhost/oauth/token: x509: certificate signed by unknown authority
Post https://localhost/oauth/token: x509: certificate signed by unknown authority
Я использую тот же ca.pem, которому доверяет ttn-stack
который я запускаю с помощью docker-compose.
Я снова справился с проблемой входа / сертификата, используя http URI и http-порты в конфигурации ttn-lw-cli
. Когда я запускаю sudo ttn-lw-cli end-device set "mysensor1app" "mysensor1dev" --net-id=000000 --log.level="debug"
, я вижу следующее:
root<strong i="8">@localhost</strong>:/etc/ttn-stack$ sudo ttn-lw-cli end-device set "mysensor1app" "mysensor1dev" --net-id=000000 --log.level="debug"
DEBUG Using access token (valid until 6:42PM)
DEBUG ccResolverWrapper: sending update to cc: {[{localhost:1884 <nil> 0 <nil>}] <nil> <nil>}
DEBUG pickfirstBalancer: HandleSubConnStateChange: 0xc000414730, {CONNECTING <nil>}
WARN grpc: addrConn.createTransport failed to connect to {localhost:1884 <nil> 0 <nil>}. Err :connection error: desc = "transport: authentication handshake failed: context deadline exceeded". Reconnecting...
DEBUG pickfirstBalancer: HandleSubConnStateChange: 0xc000414730, {TRANSIENT_FAILURE connection error: desc = "transport: authentication handshake failed: context deadline exceeded"}
DEBUG pickfirstBalancer: HandleSubConnStateChange: 0xc000414730, {CONNECTING <nil>}
WARN grpc: addrConn.createTransport failed to connect to {localhost:1884 <nil> 0 <nil>}. Err :connection error: desc = "transport: authentication handshake failed: context deadline exceeded". Reconnecting...
Вот моя конфигурация ttn-lw-cli
:
--allow-unknown-hosts="true"
--application-server-enabled="true"
--application-server-grpc-address="localhost:1884"
--ca="/root/snap/ttn-lw-stack/149/ca.pem"
--config="/etc/ttn-stack/.ttn-lw-cli.yml,/root/snap/ttn-lw-stack/149/.ttn-lw-cli.yml,/root/snap/ttn-lw-stack/149/.config/.ttn-lw-cli.yml"
--credentials-id=""
--device-claiming-server-grpc-address="localhost:1884"
--device-template-converter-grpc-address="localhost:1884"
--gateway-server-enabled="true"
--gateway-server-grpc-address="localhost:1884"
--identity-server-grpc-address="localhost:1884"
--input-format="json"
--insecure="true"
--join-server-enabled="true"
--join-server-grpc-address="localhost:1884"
--log.level="info"
--network-server-enabled="true"
--network-server-grpc-address="localhost:1884"
--oauth-server-address="http://localhost/oauth"
--output-format="json"
--qr-code-generator-grpc-address="localhost:1884"
Я думаю, что это может быть связано с моей настройкой http, хотя после входа в систему у меня было сообщение INFO Got OAuth access token
которое, похоже, указывает на успешную аутентификацию.
Я также начал видеть следующую ошибку в моих журналах docker-compose
:
stack_1 | DEBUG Rejected authentication client_id=mqtt_5bc528ca.ae4ea8 error=error:pkg/ttnpb:identifiers (invalid identifiers) error_cause=error:pkg/errors:validation (invalid `application_id`: value does not match regex pattern "^[a-z0-9](?:[-]?[a-z0-9]){2,}$") field=application_id name=ApplicationIdentifiersValidationError namespace=applicationserver/io/mqtt reason=value does not match regex pattern "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" username=
stack_1 | WARN Failed to setup connection error=error:pkg/ttnpb:identifiers (invalid identifiers) error_cause=error:pkg/errors:validation (invalid `application_id`: value does not match regex pattern "^[a-z0-9](?:[-]?[a-z0-9]){2,}$") field=application_id name=ApplicationIdentifiersValidationError namespace=applicationserver/io/mqtt reason=value does not match regex pattern "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" remote_addr=172.18.0.1:57472
Я не мог понять, к чему это относится, но я подумал, что он может жаловаться на то же устройство и приложение, которые я добавил, но до сих пор не подключил датчик.
Я получаю ошибку
certificate signed by unknown authority
. Как инструментttn-lw-cli
доверяет сертификату?
Он использует CA-файл, который вы передаете с помощью ca
. Этот файл должен указывать либо на сертификат сервера (если он самоподписанный), либо на ЦС, подписавший сертификат сервера.
Вот моя конфигурация
ttn-lw-cli
:
Эта конфигурация выглядит хорошо, если вы не хотите использовать TLS. Но прослушивает ли сервер эти адреса в своей конфигурации без TLS?
Я также начал видеть следующую ошибку в моих журналах
docker-compose
:
Это клиент MQTT, подключающийся с именем пользователя, которое не является допустимым идентификатором приложения.
Спасибо за подсказки! Указание на cert.pem
вместо ca.pem
решило проблему certificate signed by unknown authority
. Однако я все еще получаю другую ошибку подключения. Я определенно слушаю порт 1884
:
user<strong i="10">@localhost</strong>:/etc/ttn-stack$ sudo netstat -tulpn | grep LISTEN
tcp6 0 0 :::1884 :::* LISTEN 18793/docker-proxy
Я также вижу, как проходят пакеты данных, когда я подключаюсь к порту 1884 через Telnet и запускаю инструмент ttn-lw-cli
. Таким образом, определенно происходит обмен пакетами, но журнал отладки по-прежнему выдает следующую ошибку: "transport: authentication handshake failed: context deadline exceeded". Reconnecting...
Я наконец решил эту проблему, добавив флаг --insecure
к команде end-device set
!! Похоже, у меня проблемы с TLS, но сейчас меня это все равно не беспокоит.
Спасибо еще раз!
Я рад сообщить вам, что после установки --root-keys.app-key.key
в дополнение к --net-id
процесс присоединения к end-device
завершился успешно, и я начал получать данные с конечного устройства на независимом Сервер приложений! Еще раз спасибо за вашу огромную помощь во всех проблемах, с которыми я столкнулся!
Замечательно! Было бы здорово, если бы вы могли задокументировать здесь свой сценарий, чтобы мы могли его включить.
Также спасибо за мотивацию и за то, что ты первый блин.