Files
mila/README.md

235 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mila
Mila — небольшая Go-библиотека для обмена данными между БВК и
медиакомплексом.
Библиотека реализует HTTP-протокол, в котором БВК отправляет в медиакомплекс
текущее состояние отображаемого контента:
```text
GET /set-content?route=<R>&str=<S>&tpl_name=<T>
GET /get-content
```
Mila намеренно не знает ничего про снапшоты, геопозицию, прогресс маршрута,
фронтенд, правила воспроизведения медиа и структуру контента White Nights. Она
только принимает, валидирует, сохраняет, логирует и возвращает состояние,
полученное от БВК.
## Установка
```bash
go get gitea.unprism.ru/KRBL/mila
```
## Зона Ответственности
Mila — библиотека протокола. Ее нужно встраивать в прикладной сервис, например
в `wn-content`, и открывать на адресе, который требуется транспортной
интеграцией.
Mila делает:
- разбирает `GET /set-content`;
- разбирает `GET /get-content`;
- валидирует `route`, `str` и `tpl_name`;
- декодирует URL-параметры стандартным HTTP-парсером Go;
- хранит последнее состояние в памяти или JSON-файле;
- возвращает JSON, совместимый с протоколом;
- логирует каждый HTTP-запрос: метод, путь, статус, удаленный адрес и время;
- вызывает опциональный callback после успешного `set-content`.
Mila не делает:
- не считает прогресс маршрута;
- не работает с геопозицией или NMEA;
- не сопоставляет `tpl_name` с реальными плейлистами;
- не читает и не пишет снапшоты;
- не общается с фронтендом напрямую;
- не решает, как именно должен отображаться контент.
## Протокол
Медиакомплекс выступает HTTP-сервером. БВК выступает HTTP-клиентом.
Адрес по документу:
```text
172.16.16.2:13280
```
Ручки:
```text
GET /set-content?route=<R>&str=<S>&tpl_name=<T>
GET /get-content
```
`set-content` принимает URL-encoded query-параметры:
| Параметр | Обязательный | Описание |
| --- | --- | --- |
| `route` | да | номер маршрута, по умолчанию 1-3 символа |
| `str` | нет | текст бегущей строки, по умолчанию до 200 символов |
| `tpl_name` | да | название шаблона медиаконтента, по умолчанию до 200 символов |
Ответ `get-content`:
```json
{
"route": "3",
"str": "Гостиный двор - Gostiny dvor",
"tpl_name": "Гостиный двор"
}
```
Поле `updated_at` хранится внутри состояния, но не отдается через
`/get-content`, потому что ответ должен оставаться строгим относительно
протокола.
## Использование
```go
package main
import (
"log"
"net/http"
"gitea.unprism.ru/KRBL/mila"
)
func main() {
store := mila.NewFileStore("/srv/persistence/client_logs/bvk_content.json")
handler := mila.NewHandler(mila.DefaultConfig(), store,
mila.WithOnSet(func(state mila.ContentState) {
// Здесь прикладная интеграция:
// - передать route/str/tpl_name в состояние приложения;
// - сопоставить state.Template с локальным шаблоном;
// - записать дополнительные логи.
}),
)
srv := &http.Server{
Addr: "0.0.0.0:13280",
Handler: handler,
}
log.Fatal(srv.ListenAndServe())
}
```
## Конфигурация
Конфигурация валидации по умолчанию:
```go
cfg := mila.DefaultConfig()
```
Сейчас это:
```go
mila.Config{
MaxRouteLen: 3,
MaxTextLen: 200,
MaxTemplateLen: 200,
}
```
Лимиты для `str` и `tpl_name` нужно подтвердить у интегратора. В переданном PDF
текстовый слой неоднозначно распознает исходное значение.
## Хранилища
`MemoryStore` подходит, когда состояние не должно переживать рестарт процесса:
```go
store := mila.NewMemoryStore()
```
`FileStore` подходит для устройства, если последнее состояние от БВК нужно
сохранять на диске:
```go
store := mila.NewFileStore("/srv/persistence/client_logs/bvk_content.json")
```
`FileStore` пишет состояние атомарно через временный файл и держит копию в
памяти после первого успешного чтения или записи.
## Callback
`WithOnSet` позволяет приложению отреагировать на валидное обновление от БВК:
```go
handler := mila.NewHandler(mila.DefaultConfig(), store,
mila.WithOnSet(func(state mila.ContentState) {
// Возможные действия на стороне приложения:
// - опубликовать номер маршрута и бегущую строку;
// - сопоставить state.Template с локальным плейлистом;
// - записать прикладные логи.
}),
)
```
Callback выполняется синхронно. Его нужно держать быстрым. Если интеграционная
логика тяжелая, лучше положить состояние во внутреннюю очередь.
## Модель Состояния
```json
{
"route": "3",
"str": "Гостиный двор - Gostiny dvor",
"tpl_name": "Гостиный двор",
"updated_at": "2026-07-12T12:00:00Z"
}
```
Go-тип:
```go
type ContentState struct {
Route string `json:"route"`
Text string `json:"str"`
Template string `json:"tpl_name"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
}
```
## Коды Ответов
| Код | Когда возвращается |
| ---: | --- |
| 200 | валидный `set-content` или готовый `get-content` |
| 400 | невалидные параметры запроса |
| 404 | неизвестный путь |
| 405 | метод не `GET` |
| 500 | ошибка хранилища или внутренняя ошибка |
| 503 | `get-content` до первого состояния, отмененный контекст, недоступная зависимость |
Формат ошибки:
```json
{
"error": "route: must be at most 3 characters"
}
```
## Тестирование
```bash
go test ./...
```
Текущие тесты покрывают:
- успешный `set-content` + `get-content`;
- `get-content` до готовности состояния;
- невалидный `route`;
- UTF-8 и лимиты для текста/шаблона;
- сохранение и чтение через `FileStore`;
- отсутствие файла состояния в `FileStore`.