Initial commit
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
# 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`
|
||||
Reference in New Issue
Block a user