2026-10-29 14:39:39 +03:00 | | | # Устройство приложения и API
|
| | |
|
| | | [Документация](README.md) · [Конфигурация](configuration.md) · [Эксплуатация](maintenance.md)
|
| | |
|
| | | ## Компоненты
|
| | |
|
| | | ```text
|
| | | Браузер → FastAPI → TerminalManager → процессы MT5 (/portable)
|
| | | → ConfigStore → личный JSON
|
| | | → MT5_Control → psutil + чтение SQLite
|
| | | Каждый MT5 → ServiceStateWriter → общая SQLite в Common/Files
|
| | | ```
|
| | |
|
| | | | Файл | Ответственность |
|
| | | |---|---|
|
| | | | `run.py` | Параметры запуска Uvicorn; `--reload` включается только явно |
|
| | | | `main.py` | Страницы, проверка входных данных, HTTP API и время жизни менеджера |
|
| | | | `config.py` | Проверка JSON, разрешение путей и атомарное сохранение |
|
| | | | `terminal_manager.py` | Создание, подключение счетов, запуск, остановка и очередь задач |
|
| | | | `mt5_control.py` | Поиск процессов по пути EXE и чтение последних данных сервиса |
|
| | | | `project_updates.py` | Проверка upstream, fast-forward, сборка и копирование сервиса |
|
| | | | `ServiceStateWriter.mq5` | Регистрация терминала и запись текущего состояния |
|
| | | | `deploy_service.py` | Первоначальный сценарий развёртывания; общая функция компиляции |
|
| | | | `templates/`, `static/` | Jinja2, локальные CSS/SVG и JavaScript без Bootstrap/jQuery |
|
| | |
|
| | | SQLite содержит `terminals` с уникальным именем папки и сведениями об установке
|
| | | и `states` с последним снимком счёта/соединения. Сервис работает независимо от
|
| | | графиков, использует WAL, тайм-аут блокировки и UPSERT. История ещё не записывается.
|
| | | Временные метки — Unix-секунды UTC (`TimeGMT()` на стороне MQL5).
|
| | |
|
| | | Python определяет наличие процесса через `psutil`. Если процесс есть и данные
|
| | | не старее 30 секунд, внутренний статус — `running`; иначе при живом процессе
|
| | | `starting`, при отсутствии — `stopped`. Интерфейс дополнительно различает
|
| | | подключённый счёт и отсутствие торгового соединения.
|
| | |
|
| | | ## Фоновые операции
|
| | |
|
| | | Длительная команда возвращает HTTP 202 и объект задачи. Это подтверждает постановку
|
| | | в очередь, а не успех. Клиент запрашивает `GET /api/jobs/{id}` до `done` или `error`.
|
| | | Задачи выполняются одним рабочим потоком; повторная команда для того же экземпляра
|
| | | пока его задача не завершена отклоняется. Глобальные операции проекта не принимаются
|
| | | при других незавершённых задачах и на время своей работы блокируют новые команды
|
| | | терминалам.
|
| | |
|
| | | ```json
|
| | | {
|
| | | "id": "идентификатор",
|
| | | "folder": "MetaTrader5.5",
|
| | | "action": "create",
|
| | | "status": "queued",
|
| | | "stage": "Ожидание",
|
| | | "warnings": []
|
| | | }
|
| | | ```
|
| | |
|
| | | Состояния: `queued`, `running`, `done`, `error`. При ошибке добавляется `error`,
|
| | | у операций проекта — также `result` при наличии результата. В глобальных задачах
|
| | | `folder` — пустая строка. Задачи находятся в памяти; старые завершённые записи
|
| | | постепенно удаляются (сохраняется примерно 50 последних). Они не переживают
|
| | | перезапуск менеджера и не являются постоянным журналом.
|
| | |
|
| | | ## Маршруты
|
| | |
|
| | | | Метод и путь | Назначение |
|
| | | |---|---|
|
| | | | `GET /` | Главная страница |
|
| | | | `GET /settings` | Настройки |
|
| | | | `POST /instances/{name}` | Текущее состояние указанного экземпляра |
|
| | | | `POST /start/{name}` | Запуск и проверка, 202 |
|
| | | | `POST /stop/{name}` | Остановка, 202 |
|
| | | | `POST /create/{name}` | Создание с именем в пути, 202; тело `AccountInput` необязательно |
|
| | | | `POST /api/terminals` | Создание, 202; имя можно предложить автоматически |
|
| | | | `PUT /api/terminals/{name}/account` | Сохранение/применение реквизитов, 202 |
|
| | | | `GET /api/jobs/{id}` | Состояние задачи; неизвестный id — 404 |
|
| | | | `GET /api/settings` | Общие параметры, записи счетов без паролей, файлы и предлагаемое имя |
|
| | | | `PUT /api/settings` | Сохранение общих настроек |
|
| | | | `PUT /api/files/{kind}` | Загрузка исходного файла как необработанного тела запроса |
|
2026-10-31 14:22:28 +03:00 | | | | `GET /api/project` | Локальные сведения Git, сборки, различий EX5 и автозапуска без сетевого fetch |
|
2026-10-29 14:39:39 +03:00 | | | | `POST /api/project/check` | Проверка удалённой ветки, 202 |
|
| | | | `POST /api/project/update` | Загрузка через fast-forward, 202 |
|
| | | | `POST /api/project/deploy` | Принудительная сборка/копирование сервиса, 202 |
|
| | |
|
| | | `AccountInput` содержит `name`, `login`, `server`, `password`, `clear_account`.
|
| | | Для создания через `/api/terminals` к нему добавляется `folder`; пустое значение
|
| | | выбирает свободное имя автоматически. Пример без счёта:
|
| | |
|
| | | ```json
|
| | | {"folder": "MetaTrader5.5", "name": "Новый экземпляр"}
|
| | | ```
|
| | |
|
| | | `PUT /api/settings` принимает полный набор редактируемых общих полей:
|
2026-10-31 06:48:02 +03:00 | | | `mt5_folder`, `terminal_template`, `mt5_installation`, `service_ex5`, `startup_timeout_sec`, `disk_warning_mb`.
|
2026-10-29 14:39:39 +03:00 | | | Остальная конфигурация сохраняется. Это не универсальный PATCH произвольного JSON.
|
| | |
|
2026-10-31 07:26:44 +03:00 | | | Пути экземпляров вычисляются как `mt5_folder / ключ в terminals` общей функцией
|
| | | `instance_directory()`. В JSON у экземпляров нет собственного `path`; старое поле
|
| | | игнорируется при чтении. В ответах API поле `path` остаётся вычисляемым значением
|
| | | для отображения, а не настройкой расположения.
|
| | |
|
2026-10-29 14:39:39 +03:00 | | | Загрузки: `kind` равен `terminal`, `editor`, `servers` или `service`.
|
| | | Используется бинарное тело, не multipart; предел для `servers` — 16 МиБ,
|
| | | для остальных — 256 МиБ. Пустые файлы отклоняются, у EXE проверяется сигнатура MZ.
|
| | | Это не полная проверка содержимого исполняемого файла.
|
| | |
|
| | | Ошибки входных типов возвращают 422 без исходного объекта и пароля; известные
|
| | | ошибки запуска задач/настроек — обычно 400. Ошибка уже принятой операции видна
|
| | | в объекте задачи при HTTP 200 на запрос её состояния. Интерактивная схема FastAPI
|
| | | доступна по `/docs`, OpenAPI — `/openapi.json`.
|
| | |
|
| | | ## Создание и авторизация
|
| | |
|
| | | Новый экземпляр получает два EXE, `config/servers.dat`, сервис и `config/services.ini`
|
| | | с именем общей базы. В JSON он регистрируется до запуска. Ожидание сервиса проверяет
|
| | | свежесть, полный путь установки и при заданном счёте — логин, сервер и подключение.
|
| | | При неудаче удаляется только новая папка, принадлежащая этой операции, и её запись.
|
| | |
|
| | | Для первого входа используются временный INI и `KeepPrivate=1` в основной
|
| | | конфигурации терминала. После успешного подключения главное окно закрывается
|
| | | через `WM_CLOSE`, временный файл удаляется, затем проверяется обычный запуск.
|
| | | Обновление существующего счёта не удаляет папку терминала при ошибке.
|
| | |
|
| | | ## Проверки разработки
|
| | |
|
| | | ```powershell
|
| | | python -m unittest discover -s tests -v
|
| | | ```
|
| | |
|
| | | `test_provisioning.py` использует временные папки и имитирует границу с MT5,
|
| | | проверяя выбор файлов, учётные записи, сохранение и откат. `test_http.py` поднимает
|
| | | изолированный сервер на свободном порту и проверяет маршруты и отсутствие паролей
|
| | | в ответах. `test_project_updates.py` работает с временным локальным Git-remote:
|
| | | проверяет fetch, fast-forward, защиту правок и распространение EX5 с имитацией ME.
|
| | | Сетевой репозиторий и рабочие терминалы пользователя тесты не меняют.
|
| | |
|
| | | Для ручной проверки реального MT5 есть `tools/smoke_create_terminal.py`.
|
| | | Он создаёт отдельную тестовую копию и базу, затем удаляет собственные файлы.
|
| | | Инструмент привязан к локальной установке `C:\MT5\MetaTrader5.1` и пути общей базы
|
| | | из `config.json`; перед использованием проверьте эти значения. Вариант
|
| | | `--with-account` запрашивает реквизиты при запуске; логин и сервер можно передать
|
| | | через `--login` и `--server`, пароль вводится скрыто через `getpass` и не передаётся
|
| | | аргументом командной строки. Файл `tools/test_first_login.py` не используется.
|
| | | Это инструмент разработчика, а не шаг установки.
|