266 lines
7.7 KiB
Markdown
266 lines
7.7 KiB
Markdown
# Model Update Server
|
||
|
||
Минимальный backend-сервис на FastAPI для загрузки `.pt` моделей, их stub-конвертации в `.ort`, хранения метаданных в SQLite и выдачи последней модели устройствам.
|
||
|
||
## Что умеет
|
||
|
||
- `POST /models` - загрузка новой `.pt` модели с версией
|
||
- `GET /models/latest` - метаданные последней модели
|
||
- `GET /models/latest/download` - скачивание последней `.ort` модели
|
||
|
||
## API
|
||
|
||
Все API-запросы требуют заголовок авторизации:
|
||
|
||
```http
|
||
X-API-Key: <server-issued-key>
|
||
```
|
||
|
||
Ключи выпускаются вручную на сервере и задаются через переменную окружения `API_KEYS`.
|
||
Можно указать один ключ или несколько ключей через запятую.
|
||
|
||
### `POST /models`
|
||
|
||
Загружает новую модель и регистрирует её как актуальную.
|
||
|
||
Принимает:
|
||
|
||
- `Content-Type: multipart/form-data`
|
||
- поле `file` - файл модели в формате `.pt`
|
||
- поле `version` - строковая версия модели, например `1.0.3`
|
||
|
||
Возвращает:
|
||
|
||
- `201 Created` и JSON:
|
||
|
||
```json
|
||
{
|
||
"version": "1.0.3",
|
||
"created_at": "2026-05-12T10:00:00Z",
|
||
"filename": "model_v1.0.3.ort",
|
||
"is_latest": true
|
||
}
|
||
```
|
||
|
||
Ошибки:
|
||
|
||
- `400` - пустая версия или файл не `.pt`
|
||
- `409` - такая версия уже существует
|
||
- `500` - ошибка конвертации
|
||
|
||
### `GET /models/latest`
|
||
|
||
Возвращает метаданные последней доступной модели.
|
||
|
||
Принимает:
|
||
|
||
- ничего
|
||
|
||
Возвращает:
|
||
|
||
- `200 OK` и JSON:
|
||
|
||
```json
|
||
{
|
||
"version": "1.0.3",
|
||
"created_at": "2026-05-12T10:00:00Z",
|
||
"filename": "model_v1.0.3.ort"
|
||
}
|
||
```
|
||
|
||
Ошибки:
|
||
|
||
- `404` - если моделей ещё нет
|
||
|
||
### `GET /models/latest/download`
|
||
|
||
Возвращает файл последней `.ort` модели.
|
||
|
||
Принимает:
|
||
|
||
- query-параметр `version`
|
||
- query-параметр `filename`
|
||
|
||
Возвращает:
|
||
|
||
- `200 OK`
|
||
- бинарный файл `.ort`
|
||
- заголовок `Content-Disposition` для скачивания
|
||
|
||
Ошибки:
|
||
|
||
- `404` - если моделей нет, файл отсутствует на диске или `version`/`filename` не совпадают с актуальной моделью
|
||
|
||
## Требования
|
||
|
||
- Python 3.11+
|
||
|
||
## Локальный запуск
|
||
|
||
```bash
|
||
python3 -m venv .venv
|
||
source .venv/bin/activate
|
||
pip install -r requirements.txt
|
||
uvicorn app.main:app --reload
|
||
```
|
||
|
||
Сервис будет доступен на `http://127.0.0.1:8000`.
|
||
|
||
Пример запуска с API-ключом:
|
||
|
||
```bash
|
||
API_KEYS="local-dev-key" uvicorn app.main:app --reload
|
||
```
|
||
|
||
Сгенерировать 10 API-ключей и экспортировать их в текущую shell:
|
||
|
||
```bash
|
||
source scripts/generate_api_keys.sh
|
||
uvicorn app.main:app --reload
|
||
```
|
||
|
||
Сгенерировать 10 API-ключей и сохранить их в env-файл:
|
||
|
||
```bash
|
||
scripts/generate_api_keys.sh --env-file .env
|
||
set -a
|
||
source .env
|
||
set +a
|
||
uvicorn app.main:app --reload
|
||
```
|
||
|
||
## Переменные окружения
|
||
|
||
- `MODELS_DIR` - директория хранения итоговых `.ort` моделей
|
||
- `TMP_DIR` - директория для временно загруженных `.pt`
|
||
- `DATABASE_URL` - URL подключения к SQLite, например `sqlite:///./db/app.db`
|
||
- `API_KEYS` - один или несколько API-ключей через запятую, например `key-1,key-2`
|
||
|
||
По умолчанию используются:
|
||
|
||
- `storage/models`
|
||
- `tmp`
|
||
- `sqlite:///db/app.db`
|
||
- пустой список API-ключей, при котором API-запросы будут отклоняться с `401`
|
||
|
||
## Архитектура
|
||
|
||
Проект разложен по слоям clean architecture:
|
||
|
||
- `app/domain` - сущности, интерфейсы репозиториев и доменные исключения
|
||
- `app/application` - use cases и DTO
|
||
- `app/infrastructure` - SQLite, файловое хранилище, stub-конвертер, bootstrap
|
||
- `app/presentation` - FastAPI routes, зависимости и response-схемы
|
||
|
||
Точка входа приложения: [app/main.py](/Users/tanexc/PyCharmMiscProject/convert-and-update-model-server/app/main.py).
|
||
|
||
## Поведение stub-конвертера
|
||
|
||
Stub-конвертер уже подключен к `POST /models`, но реальную конвертацию пока не делает. В текущей версии он копирует загруженный `.pt` файл в хранилище с расширением `.ort`, чтобы можно было проверить сценарий загрузки и скачивания end-to-end.
|
||
|
||
Точка расширения находится в [app/infrastructure/ModelConverterImpl.py](/Users/tanexc/PyCharmMiscProject/convert-and-update-model-server/app/infrastructure/ModelConverterImpl.py).
|
||
|
||
## Примеры запросов
|
||
|
||
Загрузка модели:
|
||
|
||
```bash
|
||
curl -X POST "http://127.0.0.1:8000/models" \
|
||
-H "X-API-Key: local-dev-key" \
|
||
-F "version=1.0.3" \
|
||
-F "file=@./example.pt"
|
||
```
|
||
|
||
Получение метаданных:
|
||
|
||
```bash
|
||
curl -H "X-API-Key: local-dev-key" \
|
||
"http://127.0.0.1:8000/models/latest"
|
||
```
|
||
|
||
Скачивание последней модели:
|
||
|
||
```bash
|
||
curl -OJ -H "X-API-Key: local-dev-key" \
|
||
"http://127.0.0.1:8000/models/latest/download?version=1.0.3&filename=model_v1.0.3.ort"
|
||
```
|
||
|
||
## Docker
|
||
|
||
Сборка:
|
||
|
||
```bash
|
||
docker build -t model-update-server .
|
||
```
|
||
|
||
Сгенерировать ключи в `.env`:
|
||
|
||
```bash
|
||
scripts/generate_api_keys.sh --env-file .env
|
||
```
|
||
|
||
Запуск через env-файл:
|
||
|
||
```bash
|
||
docker run --rm -p 8000:8000 \
|
||
--env-file .env \
|
||
-v "$(pwd)/storage/models:/app/storage/models" \
|
||
-v "$(pwd)/db:/app/db" \
|
||
model-update-server
|
||
```
|
||
|
||
Файл `.env` содержит реальные ключи и не должен попадать в репозиторий. Шаблон без секретов находится в `.env.example`.
|
||
|
||
## systemd
|
||
|
||
Пример unit-файла находится в `deploy/systemd/model-update-server.service`.
|
||
Он читает переменные окружения из `/etc/model-update-server/model-update-server.env`.
|
||
|
||
Установка env-файла с ключами:
|
||
|
||
```bash
|
||
sudo mkdir -p /etc/model-update-server
|
||
scripts/generate_api_keys.sh --env-file /tmp/model-update-server.env
|
||
sudo install -m 600 /tmp/model-update-server.env /etc/model-update-server/model-update-server.env
|
||
rm /tmp/model-update-server.env
|
||
```
|
||
|
||
Установка unit-файла:
|
||
|
||
```bash
|
||
sudo cp deploy/systemd/model-update-server.service /etc/systemd/system/model-update-server.service
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now model-update-server
|
||
```
|
||
|
||
## CI
|
||
|
||
В CI храните `API_KEYS` как secret/protected variable и передавайте её в окружение сборки или деплоя.
|
||
Не генерируйте новые ключи при каждом деплое, если старые клиенты должны продолжать работать.
|
||
|
||
Пример для GitHub Actions:
|
||
|
||
```yaml
|
||
env:
|
||
API_KEYS: ${{ secrets.API_KEYS }}
|
||
```
|
||
|
||
Пример для GitLab CI:
|
||
|
||
```yaml
|
||
deploy:
|
||
script:
|
||
- docker run --env API_KEYS="$API_KEYS" model-update-server
|
||
```
|
||
|
||
## Структура данных
|
||
|
||
Таблица `models`:
|
||
|
||
- `id`
|
||
- `version`
|
||
- `filename`
|
||
- `file_path`
|
||
- `created_at`
|
||
- `is_latest`
|