mt5-manager/main.py
Yuriy Bykov Yuriy Bykov dbed2c48f9 Edit article part 4, add testing section and screenshots
Article:
- edit all sections, shorten schema drift story,
  drop the useful-links and warning sections, keep first-level TOC only
- add "Тестирование" with three 750 px screenshots (docs/images)
- add "Содержание архива" with file versions

Code:
- ServiceStateWriter.mq5: use TERMINAL_DATA_PATH (portable-only), replace
  ExecutePragma() with DatabaseExecute(), use built-in
  ERR_DATABASE_NO_MORE_DATA, align comments with the article
- deploy_service.py: skip compilation when .ex5 is newer than the source
- bump versions: main.py 0.3.0, config.py 0.2.0, static/script.js 0.2.0

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 21:19:21 +03:00
84 lines
3.4 KiB
Python
'''
File: main.py
Description: Веб-приложение для веб-сервера терминалов, основной файл FastAPI
'''
__version__ = '0.3.0'
# Импортируем нужные классы из библиотек
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, Path
from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from pydantic_settings import BaseSettings, JsonConfigSettingsSource, SettingsConfigDict
from config import Config
from mt5_control import MT5_Control
@asynccontextmanager
async def lifespan(app: FastAPI):
# Создаем объект, читающий конфигурацию из JSON-файла
config = Config(_env_file=None)
# Переносим прочитанное в объект app
app.state.terminals = config.terminals
app.state.instances = {}
# Обеспечиваем наличие имени экземпляра терминала.
# При отсутсвии его в конфигурации подставляется имя папки
for folder in app.state.terminals:
app.state.terminals[folder]['name'] = app.state.terminals[folder].get(
'name', folder)
# Создаём глобальный объект управления терминалами
global control
control = MT5_Control(app.state.terminals,
config.mt5_folder, config.database, config.mt5_exe)
yield
# Создаём объект приложения
app = FastAPI(lifespan=lifespan)
# Подключаем статические файлы
app.mount('/static', StaticFiles(directory='static'), name='static')
# Создаём объект для работы с шаблонами HTML-кода
templates = Jinja2Templates(directory='templates')
@app.get('/', response_class=HTMLResponse)
async def index(request: Request):
'''Обработчик GET / (корневой каталог) - панель управления терминалами'''
instances = control.load_instances()
return templates.TemplateResponse(request, 'index.html', {'instances': instances})
@app.post('/instances/{name}')
async def info_instance(name: str = Path(..., description="Информация об экземпляре", examples=["MetaTrader5.1"])):
'''Получение информации об экземпляре терминала'''
result = control.instance_info(name)
return JSONResponse(result)
@app.post('/start/{name}')
async def start_instance(name: str = Path(..., description="Название экземпляра", examples=["MetaTrader5.1"])):
'''Запуск экземпляра терминала'''
result = control.start_mt5(name)
return JSONResponse(result)
@app.post('/stop/{name}')
async def stop_instance(name: str = Path(..., description="Название экземпляра", examples=["MetaTrader5.1"])):
'''Остановка экземпляра терминала'''
result = control.stop_mt5(name)
return JSONResponse(result)
@app.post('/create/{name}')
async def create_instance(name: str = Path(..., description="Название экземпляра", examples=["MetaTrader5.1"])):
'''Создание экземпляра терминала'''
result = control.create_mt5(name)
return JSONResponse(result)