Потоки даних
Критичні наскрізні сценарії, відомі провайдери підставлені (Monobank, Google OAuth, Cloudflare Origin Pulls, Typesense).
Автентифікація — Google OAuth + JWT
Section titled “Автентифікація — Google OAuth + JWT”sequenceDiagram
actor U as Абітурієнт
participant W as Abitly Web
participant A as Abitly API (NestJS)
participant G as Google OAuth
participant DB as Postgres (shared)
U->>W: Login → Google
W->>G: redirect OAuth (client_id, redirect_uri)
G-->>U: вибір акаунту, consent
U->>G: підтверджує
G-->>W: callback з code
W->>A: POST /auth/google {code}
A->>G: exchange code → tokens
G-->>A: id_token / refresh_token
A->>DB: upsert user
A-->>W: JWT access + refresh cookies
W-->>U: авторизовано
Альтернатива: Telegram-прив’язка через бот — deep-link link_<token> редімить одноразовий Redis-токен (мінтить бекенд) і лінкує акаунт (telegram_users.web_user_id). Наскрізно ще не працює — див. картку бота.
Оплата — Monobank
Section titled “Оплата — Monobank”sequenceDiagram
actor U as Абітурієнт
participant W as Web / Mini App
participant A as Abitly API
participant M as Monobank API
U->>W: Обирає Premium / Token purchase
W->>A: POST /payments/create {plan}
A->>M: create invoice (MONOBANK_TOKEN)
M-->>A: invoiceId + paymentUrl
A-->>W: paymentUrl
W-->>U: redirect на Monobank checkout
U->>M: оплачує
M-->>A: webhook (SUBSCRIPTION_WEBHOOK_URL / PREMIUM_WEBHOOK_URL / TOKEN_PURCHASE_WEBHOOK_URL)
A->>A: verify signature, активувати підписку
A-->>W: redirect (SUBSCRIPTION_REDIRECT_URL / ...)
Контент Strapi — через Cloudflare Authenticated Origin Pulls
Section titled “Контент Strapi — через Cloudflare Authenticated Origin Pulls”sequenceDiagram
actor U as Абітурієнт
participant W as Abitly Web
participant CF as Cloudflare proxy
participant S as Strapi (EC2)
participant DB as Postgres
U->>W: Відкриває сторінку ЗВО
W->>CF: GET /api/universities/...
CF->>S: запит з origin-cert<br/>(валідація ORIGIN_PULL_CA)
S->>S: перевірка cert OK
S->>DB: SELECT (схема strapi)
DB-->>S: рядки
S-->>CF: JSON
CF-->>W: cached / fresh
W-->>U: відрендерена сторінка
403 від Strapi → найімовірніше прострочений CF origin-cert. Деталі → domains-dns.
Telegram bot + Mini App
Section titled “Telegram bot + Mini App”sequenceDiagram
actor U as Користувач
participant T as Telegram API
participant B as Bot (aiogram · AWS ECS)
participant DB as Postgres (abitly)
participant A as Abitly API
participant M as Mini App (Next.js · Railway)
U->>T: /start
T->>B: update (long polling)
B->>DB: upsert TelegramUser
DB-->>B: профіль / бали
B->>T: sendMessage (+ web_app button)
T-->>U: повідомлення + кнопка
U->>T: tap «Open App»
T->>M: launch Mini App with initData
M->>A: POST /telegram/auth {initData}
A->>A: verify HMAC(initData, bot_token)
A-->>M: JWT
M-->>U: інтерактивний UX
Повний набір сценаріїв бота (use-case, sequence по кожній команді/deep-link, state, доменна UML) → Telegram-бот: user-сценарії.
Підтримка — CRM/Support bot (омніканальні звернення)
Section titled “Підтримка — CRM/Support bot (омніканальні звернення)”Один Lambda-вебхук на три канали. Telegram-гілка — інтейк-візард; Meta-гілка (Instagram Direct / Messenger) — топік з першого DM. Деталі й обмеження → картка сервісу.
sequenceDiagram
actor IGU as IG-користувач
actor OP as Оператор
participant MG as Meta Graph API
participant GW as API GW /meta
participant L as Lambda abitly-crm-bot
participant D as DynamoDB
participant SR as Форум-група «Abitly CRM»
IGU->>MG: DM до @abitly_org
MG->>GW: POST (X-Hub-Signature-256)
GW->>L: подія messages
L->>L: verify HMAC + дедуп mid
L->>D: get/create CONVO (CHAN#ig#IGSID)
L->>SR: createForumTopic «🔵📸 Ім'я @username» + текст
L->>D: LOG# + MAP mid↔message_id
OP->>SR: відповідь у топіку
SR->>L: Telegram webhook (topicId)
L->>MG: POST /me/messages (RESPONSE ≤24h / HUMAN_AGENT ≤7d)
MG-->>IGU: відповідь у Direct
L->>SR: топік → ⚪️
Аналітика — lake-house
Section titled “Аналітика — lake-house”sequenceDiagram
participant W as Abitly Web
participant GW as API Gateway HTTP<br/>(abitly-collector-dev)
participant L as Lambda collector
participant F as Kinesis Firehose
participant S as S3 Iceberg/Parquet
participant DB as Athena + Glue Catalog
W->>GW: POST /collect {events:[...]}
GW->>L: invoke
L->>L: Ajv validate vs events.schema.json
L->>L: dedup + enrich
L->>F: PutRecordBatch
F->>S: Parquet partitions
DB-.->S: каталог
Note over DB: dbt models<br/>→ Metabase dashboards
Поточний стан — dev only, prod ще не задеплоєно. Деталі → analytics.
Cross-product search (Abitly → Studsearch)
Section titled “Cross-product search (Abitly → Studsearch)”sequenceDiagram
actor U as Абітурієнт
participant W as Abitly Web
participant A as Abitly API
participant S as Studsearch backend
participant DB as Postgres<br/>(shared, різні схеми)
U->>W: пошук ЗВО з insider-відгуками
W->>S: GET /search?q=...
Note over W,S: NEXT_PUBLIC_STUDSEARCH_API_URL
S->>DB: SELECT FROM studsearch.*
DB-->>S: відгуки
S-->>W: JSON
W->>A: GET /universities/...
A->>DB: SELECT FROM abitly.*
DB-->>A: дані ЗВО
A-->>W: JSON
W-->>U: об'єднаний UX
Один і той самий Postgres, дві різні схеми, два різні API.
Маркетинг — grounded AI-кампанія + атрибуція
Section titled “Маркетинг — grounded AI-кампанія + атрибуція”sequenceDiagram
participant SCH as Marketing<br/>(BullMQ)
participant DB as marketing schema<br/>(read-model)
participant AI as Claude (L2/L3)
participant V as Validator
participant ST as Strapi (facts)
participant ESP as ESP / @abitlybot
actor C as Контакт
participant LH as Lake-house
participant M as Monobank
SCH->>DB: SELECT сегмент (lifecycle × intent)
SCH->>AI: згенерувати копію (structured input)
AI-->>SCH: обгортка зі слотами
SCH->>V: перевірити (нема вигаданих фактів)
V-->>SCH: ok / regenerate
Note over SCH: approval queue (людина схвалює L2)
loop на кожен контакт
SCH->>ST: факти (дедлайн, бал, ціна)
SCH->>DB: suppression + freq cap + consent?
SCH->>ESP: send (campaign_id, contact_id)
ESP-->>C: лист / повідомлення<br/>(unsubscribe + preference-center)
ESP->>LH: delivered / opened / clicked
end
C->>M: оплата (Premium / Sub / Tokens)
M->>LH: webhook → last-touch campaign_id
Note over LH: mart: sends→clicks→purchases<br/>= attributed revenue (D1)
Факти ніколи не генеруються моделлю — лише підставляються у слоти (ADR-3). Поточний стан — план (abitly-marketing).