SVG Service

Backend-сервис для загрузки, sanitization, normalization, хранения и базовой эксплуатации SVG-схем с местами, секторами, группами и pricing.

Текущее состояние

Реализовано:

  • загрузка SVG-файла
  • безопасная первичная обработка SVG
  • sanitization запрещённых элементов и атрибутов
  • normalization в машиночитаемый JSON snapshot
  • хранение original / sanitized / normalized файлов
  • PostgreSQL persistence
  • registry uploads / schemes / versions
  • lifecycle схем:
    • draft
    • publish
    • unpublish
    • rollback
    • create next draft version
  • persistence доменных сущностей:
    • sectors
    • groups
    • seats
  • pricing v1:
    • pricing categories
    • price rules
    • effective price resolution
  • test mode preview API
  • audit/history API

Стек

  • Python 3.11+
  • FastAPI
  • Pydantic
  • SQLAlchemy Async
  • PostgreSQL
  • Alembic
  • Docker Compose

Запуск

1. Сборка и запуск

docker compose build --no-cache docker compose up -d

2. Применение миграций

docker compose run --rm svg-service alembic -c /app/alembic.ini upgrade head

3. Проверка

curl -s http://127.0.0.1:9020/healthz

Ожидаемый ответ:

{"status":"ok"}


Аутентификация

Во все защищённые эндпойнты передаётся заголовок:

X-API-Key:

Локально использовались ключи из .env.


Основные env-переменные

Примерно используются такие параметры:

  • APP_NAME
  • APP_ENV
  • APP_PORT
  • API_V1_PREFIX
  • AUTH_HEADER_NAME
  • ADMIN_API_KEY
  • VIEWER_API_KEY
  • SVG_MAX_FILE_SIZE_BYTES
  • SVG_MAX_ELEMENTS
  • SVG_ALLOW_INTERNAL_USE_REFERENCES_ONLY
  • SVG_FORBID_FOREIGN_OBJECT_V1
  • SVG_FORBID_STYLE_V1
  • SVG_FORBID_IMAGE_V1
  • POSTGRES_HOST
  • POSTGRES_PORT
  • POSTGRES_DB
  • POSTGRES_USER
  • POSTGRES_PASSWORD
  • DATABASE_URL

Storage layout

На файловой системе используются каталоги:

  • storage/original
  • storage/sanitized
  • storage/normalized

Каждая загрузка сохраняется в свой каталог по upload_id.


API

1. Service / auth / diagnostics

GET /

Базовая информация о сервисе.

GET /healthz

Healthcheck.

GET /api/v1/ping

Проверка API и auth.

GET /api/v1/auth/me

Возвращает текущую роль по API key.

GET /api/v1/db/ping

Проверка доступности PostgreSQL.

GET /api/v1/manifest

Возвращает manifest сервиса:

  • лимиты SVG
  • правила sanitization
  • extraction contract

2. Uploads

POST /api/v1/schemes/upload

Загружает SVG, выполняет:

  • validate
  • inspect
  • sanitize
  • normalize
  • storage save
  • create upload record
  • create scheme
  • create version 1
  • persist sectors/groups/seats
  • audit event

Form-data

  • file: SVG-файл

Ответ

Возвращает:

  • upload_id
  • имя файла
  • размеры
  • counts
  • storage paths
  • accepted flag

GET /api/v1/uploads

Список upload records.

GET /api/v1/uploads/{upload_id}

Детали upload record.

GET /api/v1/uploads/{upload_id}/normalized

Чтение normalized JSON snapshot.


3. Schemes

GET /api/v1/schemes

Список схем.

GET /api/v1/schemes/{scheme_id}

Детали схемы.

GET /api/v1/schemes/{scheme_id}/current

Текущая version схемы.

GET /api/v1/schemes/{scheme_id}/versions

Список всех version схемы.

POST /api/v1/schemes/{scheme_id}/versions

Создаёт новую draft version из current version.

Что делает:

  • создаёт version_number = current + 1
  • копирует metadata snapshot
  • копирует sectors/groups/seats
  • переключает current_version_number
  • сбрасывает схему в draft
  • пишет audit event

4. Scheme lifecycle

POST /api/v1/schemes/{scheme_id}/publish

Публикует current version.

Что делает:

  • scheme.status = published
  • published_at = now()
  • current version получает статус published
  • пишет audit event

POST /api/v1/schemes/{scheme_id}/unpublish

Снимает публикацию.

Что делает:

  • scheme.status = draft
  • published_at = null
  • current version переводится в draft
  • пишет audit event

POST /api/v1/schemes/{scheme_id}/rollback

Откатывает current version на указанную version.

JSON body

{ "target_version_number": 1 }

Что делает:

  • переключает current_version_number
  • сбрасывает публикацию
  • схема становится draft
  • пишет audit event

5. Current domain data

GET /api/v1/schemes/{scheme_id}/current/sectors

Сектора текущей версии.

GET /api/v1/schemes/{scheme_id}/current/groups

Группы текущей версии.

GET /api/v1/schemes/{scheme_id}/current/seats

Места текущей версии.

Seat содержит:

  • seat_id
  • sector_id
  • group_id
  • row_label
  • seat_number
  • geometry fields
  • tag / classes

6. Pricing

6.1 Pricing categories

POST /api/v1/schemes/{scheme_id}/pricing/categories

Создать pricing category.

JSON body

{ "name": "VIP", "code": "VIP" }

PUT /api/v1/schemes/{scheme_id}/pricing/categories/{pricing_category_id}

Обновить pricing category.

JSON body

{ "name": "Econom Plus", "code": "ECO_PLUS" }

DELETE /api/v1/schemes/{scheme_id}/pricing/categories/{pricing_category_id}

Удалить pricing category.


6.2 Price rules

POST /api/v1/schemes/{scheme_id}/pricing/rules

Создать price rule.

JSON body

{ "pricing_category_id": "....", "target_type": "sector", "target_ref": "vip", "amount": "2500.00", "currency": "RUB" }

Ограничения v1

  • target_type только:
    • sector
    • group
    • seat
  • currency только RUB
  • amount хранится как NUMERIC(12,2)

PUT /api/v1/schemes/{scheme_id}/pricing/rules/{price_rule_id}

Обновить rule.

DELETE /api/v1/schemes/{scheme_id}/pricing/rules/{price_rule_id}

Удалить rule.

GET /api/v1/schemes/{scheme_id}/pricing

Вернуть все pricing categories и rules схемы.


7. Effective pricing

GET /api/v1/schemes/{scheme_id}/current/seats/{seat_id}/price

Рассчитать effective price для места.

Приоритет поиска rules

  1. seat
  2. group
  3. sector

Ответ

Возвращает:

  • seat_id
  • sector_id
  • group_id
  • matched_rule_level
  • matched_target_ref
  • pricing_category_id
  • amount
  • currency

Если правило не найдено:

  • 404 No pricing rule matched current seat

Если место не найдено:

  • 404 Seat not found in current scheme version

8. Test mode

GET /api/v1/schemes/{scheme_id}/test/seats/{seat_id}

Preview для frontend test mode.

Возвращает:

  • seat metadata
  • selectable
  • has_price
  • pricing info, если найдено effective rule

Логика:

  • если price найден -> selectable = true
  • если price не найден -> selectable = false

9. Audit

GET /api/v1/schemes/{scheme_id}/audit

История событий схемы.

Сейчас в audit пишутся:

  • scheme.created_from_upload
  • scheme.published
  • scheme.unpublished
  • scheme.rolled_back
  • scheme.version.created
  • pricing.category.created
  • pricing.category.updated
  • pricing.category.deleted
  • pricing.rule.created
  • pricing.rule.updated
  • pricing.rule.deleted

Пример базового сценария

1. Загрузить SVG

curl -s -H 'X-API-Key: admin-local-dev-key'
-F 'file=@sample-contract.svg;type=image/svg+xml'
http://127.0.0.1:9020/api/v1/schemes/upload

2. Получить список схем

curl -s -H 'X-API-Key: admin-local-dev-key'
http://127.0.0.1:9020/api/v1/schemes

3. Создать pricing category

curl -s -X POST -H 'Content-Type: application/json' -H 'X-API-Key: admin-local-dev-key'
-d '{"name":"VIP","code":"VIP"}'
http://127.0.0.1:9020/api/v1/schemes/<scheme_id>/pricing/categories

4. Создать pricing rule

curl -s -X POST -H 'Content-Type: application/json' -H 'X-API-Key: admin-local-dev-key'
-d '{"pricing_category_id":"<category_id>","target_type":"sector","target_ref":"vip","amount":"2500.00","currency":"RUB"}'
http://127.0.0.1:9020/api/v1/schemes/<scheme_id>/pricing/rules

5. Проверить цену места

curl -s -H 'X-API-Key: admin-local-dev-key'
http://127.0.0.1:9020/api/v1/schemes/<scheme_id>/current/seats/seat-a1/price

6. Проверить test mode preview

curl -s -H 'X-API-Key: admin-local-dev-key'
http://127.0.0.1:9020/api/v1/schemes/<scheme_id>/test/seats/seat-a1


Ограничения текущего v1

  • редактирование seats/groups/sectors через API пока не реализовано
  • нет bulk update editor API
  • нет optimistic locking
  • нет soft delete policy
  • нет полноценной multi-user edit coordination
  • routes пока не разнесены по отдельным router-модулям
  • нет полного integration/e2e test набора
  • frontend отсутствует

Что делать дальше

Приоритет 1

Разработать frontend / test UI:

  • список схем
  • просмотр current seats/sectors/groups
  • test mode click preview
  • pricing management UI

Приоритет 2

Добавить editor operations:

  • update seat/group/sector in draft version
  • bulk updates
  • draft save flow

Приоритет 3

Усилить backend:

  • router decomposition
  • service-layer cleanup
  • pagination/filters for audit
  • integration tests
  • concurrency policy
  • publish validation rules
Description
No description provided
Readme 414 KiB
Languages
Python 79.4%
Shell 20.4%