Usługi
Backend Trans-Atlas: Sprint 0
Plan przekształcenia demonstracyjnego SPA w aplikację z serwerowym źródłem prawdy: konta firm, trwałe zgłoszenia, słowniki w bazie danych, dziennik zdarzeń.
To specyfikacja i zapis już przeprowadzonych kontroli artefaktów, a nie podłączony backend: index.html nadal jest jednostronicowym plikiem bez etapu budowania, bez PostgreSQL, bez serwera NestJS i bez prawdziwego API. Sekcja „Planowane zmiany we frontendzie” opisuje kolejny krok, a nie to, co już zastosowano - LISTINGS, logowanie przez localStorage i test-data/ nadal działają tak samo, jak przed tą stroną.
Zakres Sprintu 0
- PostgreSQL 18 + PostGIS: схема, міграції, довідники з реєстрів SPA
- Автентифікація: реєстрація компанії, вхід, ротація refresh-токенів, підтвердження email, ролі
- CRUD заявок і стрічка з фільтрами, що повторюють filteredListings()
- Імпорт наявних 10 773 демо- і тестових заявок у БД без втрати даних
- Append-only журнал подій
- Розгортання, міграції, бекапи, моніторинг
Świadomie poza zakresem
| Co |
Dlaczego |
Przeniesiono do |
| Модуль SEARCH (маршрути, магістраль→фідери, POST_DRONE) | Власний REST-контракт і власний CI | Окремий етап |
| Верифікація контрагента (KYB, реєстри, дельта-моніторинг) | Напрям A плану | Тижні 3–7 |
| Ingest-агент | Напрям B плану | Тижні 4–8 |
| SSE-стрічка, push, PWA | Напрям C плану | Тижні 6–10 |
| Ставки, торг, ескроу, розрахунки | Межа модулів A.1.3 рамкового ТЗ | Після перших угод |
Stos i środowisko
| Komponent |
Decyzja |
Dlaczego |
| Мова | TypeScript, Node.js 24 LTS | Одна мова від схеми до інтерфейсу |
| Каркас | NestJS | Модульність, DI, готова інтеграція валідації та OpenAPI |
| БД | PostgreSQL 18 + PostGIS 3.6 | PostGIS потрібен уже зараз для країн і відстаней |
| Кеш і черга | Redis 7 | Ідемпотентність, ліміти запитів, надалі шина подій стрічки |
| Міграції | node-pg-migrate або Flyway, тільки вперед | Застосовність до продової бази без ручних кроків |
| Пошта | SMTP із SPF/DKIM/DMARC | Підтвердження email і скидання пароля |
| Спостережуваність | JSON-логи, /api/v1/health, Prometheus | Мінімум, без якого продакшн не запускається |
Kluczowe niezmienniki na poziomie bazy danych
| Niezmiennik |
Mechanizm |
| mode='multi' ⇒ components містить ≥ 2 плеча, лише базові види | CHECK listings_components_rule |
| mode<>'multi' ⇒ components IS NULL | той самий CHECK |
| mode='drone' ⇔ існує рядок у listing_drone_details | відкладений CONSTRAINT TRIGGER |
| Місто відправлення ≠ місто призначення | CHECK listings_origin_ne_destination |
| is_cross_border не редагується вручну | GENERATED ALWAYS AS ... STORED |
| Рівень верифікації вище L0 неможливий без дати перевірки | CHECK companies_verified_needs_date |
| У компанії рівно один власник | частковий UNIQUE INDEX по role='owner' |
| Email унікальний без урахування регістру | тип citext |
| Журнал подій не редагується застосунком | REVOKE UPDATE, DELETE для ролі застосунку |
Zgodność filtrów API i SPA
GET /api/v1/listings musi zwracać ten sam wynik co dzisiejsze filteredListings().
| state.filters w SPA |
Parametr API |
state.filters.mode | mode |
state.filters.multiModes | components |
state.filters.from | origin_city_id |
state.filters.to | destination_city_id |
state.filters.fromCountry | origin_country |
state.filters.toCountry | destination_country |
state.filters.borderScope | border_scope |
state.filters.currency | currency |
Uwierzytelnianie i bezpieczeństwo
| Wymaganie |
Wartość |
| Хешування пароля | argon2id, m=64 MiB, t=3, p=1 |
| Мінімальна довжина пароля | 10 символів; перевірка за списком скомпрометованих |
| Access-токен | JWT, 15 хв, EdDSA, kid для ротації ключів |
| Refresh-токен | 30 днів, лише SHA-256; ротація з родиною, повторне використання вбиває всі сесії |
| Блокування | 10 невдалих спроб → блок на 15 хвилин |
| Підтвердження email | Одноразовий токен, 24 години; до підтвердження - перегляд без публікації |
| Ролі | owner, manager, viewer; редагувати заявку може лише її компанія |
Wymagania niefunkcjonalne
| Wskaźnik |
Cel |
| Стрічка з фільтрами, p95 | ≤ 200 мс на 100 тис. заявок |
| Публікація заявки, p95 | ≤ 300 мс |
| Доступність | 99.5% на місяць |
| Бекап | Щодоби повний, WAL безперервно, відновлення - щомісяця |
| Міграції | Тільки вперед, без ручних кроків |
Rzeczywiste przebiegi testów
| Przebieg |
Co sprawdzał |
Wynik |
tools/extract_registries.py | Витягування реєстрів із index.html | 175 міст, 9 вантажів, 32 валюти, порожніх полів немає |
ci/check_backend_contract.py | 8 груп перевірок артефактів | 15 із 15 пройдено |
ci/acceptance_db.py | Жива PostgreSQL 18.6 + PostGIS 3.6 | 25 із 25 пройдено |
Planowane zmiany we frontendzie
Kolejny krok po pojawieniu się prawdziwego backendu - niezastosowany w tym demo:
- LISTINGS перестає бути літералом і стає результатом GET /api/v1/listings; loadExternalListings() і LISTINGS_FEED замінюються курсорною пагінацією
- getUsers(), setUsers(), saveUser() видаляються. localStorage лишається лише для мови, вибраного виду транспорту та інших втрачуваних станів UI
- Форма модального вікна отримує третій режим — публікацію заявки
- Помилки показуються з problem+json, поле в помилці підсвічується за JSON Pointer
- Перемикач API_BASE: за відсутності бекенду сайт чесно повідомляє про це, як уже роблять вкладки погоди й кордонів
Bramki CI: wymagane kontrole
Dwanaście wymaganych kontroli z trzech potoków; każdy potok ma własny job-bramkę, który kończy się niepowodzeniem, jeśli zawiedzie dowolny zależny od niego job.
| Potok |
Job |
Kontrola w regule |
.github/workflows/contract.yml |
schema |
Схема і сценарії T16-T27 |
|
i18n |
Валютні та мовні сценарії C1-C13, L1-L12 |
|
proto |
Контракт proto модуля SEARCH |
|
ui |
UI-сценарії U1-U8 вибору виду транспорту |
|
contract-gate |
contract-gate |
.github/workflows/backend.yml |
backend-contract |
Контракт бекенду Спринту 0 |
|
backend-db |
Приймальні тести БД Спринту 0 |
|
verification-contract |
Контракт модуля верифікації |
|
verification-db |
Інваріанти верифікації |
|
backend-gate |
backend-gate |
.github/workflows/web3.yml |
web3-tests |
web3-sprint0-tests |
|
blockchain-gate |
blockchain-gate |
To zastosowana konfiguracja, a nie projekt: reguła gałęzi main wymaga wszystkich dwunastu kontroli wymienionych w tabeli powyżej, a `.github/workflows/` zawiera trzy wymagane potoki - `contract.yml`, `backend.yml` i `web3.yml` - oraz nieblokujący `source-probe.yml`. Skrypty `sprint-0-backend/ci/*`, które wywołuje potok backendu, są w repozytorium i uruchamiają się na prawdziwej PostgreSQL z PostGIS: w przebiegu na main przeszły migracja, zasianie słowników, import 10773 ogłoszeń i negatywne testy niezmienników - łącznie 42 kontrole na żywej bazie. `ci/check_live_ruleset.py` porównuje plik reguły z żywym GitHubem i na obecnym main zgłasza zgodność: 12 kontroli w pliku, 12 w GitHubie. Walidator spójności `ci/check_ruleset.py` też się uruchamia: 14 reguł, 35 z 35 mutacji wykrytych. Czego nadal nie zastosowano: sama ta strona pozostaje jednym plikiem bez backendu - potoki sprawdzają kontrakty i bazę, a nie obsługują index.html.
Pliki modelu