# Mindfly API v2

Чистий Laravel 11 REST-переписаний бекенд Mindfly. Працює **паралельно** зі
старим flat-file `/api` (той самий MySQL + Redis), щоб фронтенд мігрував поекранно.

- Гайд для фронтенду: [`CHANGELOG_FRONTEND.md`](CHANGELOG_FRONTEND.md)
- Безпека / ротація секретів: [`SECURITY.md`](SECURITY.md)
- Телеметрія / аналітика: [`ANALYTICS.md`](ANALYTICS.md)
- Продуктивність і надійність: [`PERFORMANCE.md`](PERFORMANCE.md)

---

## Стек

Laravel 11 · PHP 8.2+ · MySQL (наявна БД `mindfly_base`, без міграцій схеми) ·
Redis (спільний із `/api`, ключі кешу без префікса) · phpredis · GD · OpenSSL.

## Встановлення

```bash
cd api2
composer install
cp .env.example .env
php artisan key:generate
# заповнити .env: DB_*, REDIS_*, і ВСІ секрети з блоку "SECRETS" (див. SECURITY.md)
php artisan config:cache
```

### Схема БД

Прод-БД вже має всі доменні таблиці (керується легасі `/api`). Міграції в
`database/migrations/` описують цю саму схему через Laravel Blueprint і **кожна
обгорнута в `Schema::hasTable()`** — тож `php artisan migrate` безпечно ганяти
і на проді (нічого не чіпає), і на чистій CI-БД, і на SQLite для тестів.

```bash
php artisan migrate          # створить лише те, чого немає
php artisan migrate:fresh    # (dev/CI) повна пересборка схеми
```

Таблиці: `users`, `users_settings`, `sessions`, `passwordResets`, `tests`,
`questions`, `answers_{single,multiple,short,connect}`, `topics`, `materials`,
`results`, `results_hw`, `homeworks`, `subscriptions`, `payments`, `promocodes`,
`promocodes_activations`, `partners`, `banners` + фреймворкові (`cache`, `jobs`, …).

### Тести

```bash
php artisan test            # PHPUnit, SQLite :memory:, RefreshDatabase
```

`tests/Unit/GradingServiceTest.php` — чистий рушій оцінювання (single/multiple/
short/connect, NMT-шкалювання, forceMaxScore).
`tests/Feature/*` — auth, study, attempts (+ симуляції), homework, editor,
promo/leaderboard — через реальний HTTP-стек і envelope-контракт.
Хелпери для легасі-схеми: `tests/Concerns/SeedsDomain.php`.

## Запуск

```bash
php artisan serve            # локально, http://127.0.0.1:8000/api2/...
# прод: document root -> api2/public, PHP-FPM; шлях монтування задається API_PREFIX
```

Прод-чекліст швидкості (окрім самого коду — див. `PERFORMANCE.md`):

```bash
php artisan config:cache && php artisan route:cache && php artisan event:cache
```
+ увімкнений OPcache (`opcache.enable=1`, `opcache.validate_timestamps=0` на релізі,
скидати кеш байткоду при деплої), PHP-FPM `pm=dynamic` з розумним `pm.max_children`,
Redis і MySQL на тій самій підмережі, що й застосунок.

Cron (замість легасі-воркерів):

```
* * * * *  php /path/to/api2/artisan schedule:run
```

- `mindfly:renew-subscriptions` — щогодини (було `payments/subscriptionWorker.php`)
- `mindfly:sitemap` — щодня 04:00 (було `essentials/updateSitemap.php`)
- `mindfly:streak-nudges` — щодня 18:00 (було `streakNotificationsWorker.php`)

Push-нотифікації: як і раніше, кладуться у Redis-список `queue:jobs`; зовнішній
воркер не змінюється.

---

## Статус реалізації

Це **фаза 1**: каркас + ядро доменів реалізовані чисто й повністю; частину
допоміжних ручок винесено в беклог. Код **не проганявся в рантаймі** в цьому
середовищі (немає PHP/Composer/БД) — потрібен прогін на стейджі з реальною БД
перед перемиканням клієнтів.

### Готово

| Домен | Ручки |
|---|---|
| Auth | register, login, logout, password reset (+status), Google web/mobile, Apple |
| Me | профіль, налаштування, видалення акаунта, девайси, reset-stats, rerun, saved-tests, stats (v2), nmt-calculator |
| Каталог/навчання | subject tests (nmt/zno/my/org/topics), topics, material, `tests/{slug}` (+ hw code, premium/deadline гейти), answers, score-table, bulk, report, blitz, practice |
| Спроби | `POST attempts` (оцінка+збереження), `POST attempts/check`, `internal/attempts/evaluate` |
| Домашки | create, list, show, update, delete, results, analytics (item analysis / D-index), completions |
| Лідерборд, пошук | повністю |
| Оплати | subscription checkout/cancel, mono webhook, promo redeem/preview/create |
| Редактор | show/save/delete тесту (транзакційно, з авторизацією), show/save матеріалу, hardened upload |
| Вебхуки | mono, mail |

### Фаза 3 — телеметрія проходжень і таргетоване навчання

Повна фіксація мікроповедінки під час тесту + аналітичний шар. Контракт для
фронтенду: **`ANALYTICS.md`**.

- `attempts` / `attempt_events` / `attempt_question_stats` — кожне проходження
  зберігається з подіями (затримки, зміни відповідей, навігація, відкриття
  пояснень) і плоским per-question rollup для швидких агрегатів.
- `user_skill_mastery` — rolling EWMA-модель засвоєння по 3 вимірах:
  тема / позиція питання в предметі / тип питання. Оновлюється на кожному сабміті.
- Похідні сигнали: `rush` (швидко+хибно), `struggle` (довго+хибно), guessing,
  `changedFromCorrect` (друга думка), архетип спроби, `paceIndex` проти когорти,
  калібрування впевненості, аналіз дистракторів (`errorKind`, `chosenAnswer`).
- `GET /me/insights` — болючі точки (impact+recency), патерни помилок,
  misconceptions, pace-профіль, revision-due, і план: `{ reason, action, theory[], tests[] }`.
- **Порада наприкінці тесту** (`AdviceService`) — у відповіді `POST /attempts`:
  хвалить конкретне покращення, інакше м'яко підсвічує патерн; природний тон,
  не повторюється, `null` коли нема чого сказати.
- **Кабінет — дошка «над чим працювати»** (`GET/PUT /me/focus`): авто-наповнення
  зі слабких навичок + pin/dismiss; `subjects` зі статусом, `improving`, `recentAdvice`.
- **`GET /me/misconceptions`** — чому обрано конкретний дистрактор:
  `adjacent_slip` / `far_miss` / `common_trap` (найпопулярніша пастка, крос-когорта)
  / `sign_or_scale_error` / `converse_confusion` / over-underselection + пояснення.
- **Integrity / антифрод у ДЗ** (`IntegrityService`): `GET /homeworks/{code}/integrity`
  + `.../submissions/{attemptId}/integrity` — хто ймовірно рандомив (rapid_fire,
  uniform/pattern answers, score-time mismatch, `above_own_baseline` через
  крос-перевірку всієї історії учня, tab_switching). `/results` збагачено
  `integrityRisk` на кожному рядку.
- `GET /me/mastery`, `GET /homeworks/{code}/behavior` — сира модель / класова карта вагань.
- `POST /attempts/start` + `POST /attempts/{id}/events` — durable-capture під час тесту.

Приватність: гостям — 0 PII, лише анонімна поведінка; без IP/fingerprint;
`attemptId` не перехоплюється.

### Фаза 2 — зроблено

- `GET editor/tests` — каталог редактора з пагінацією + whitelisted-сортуванням.
- Staged-uploads для редактора (`UploadStagingService`): валідація → приватне
  сховище → промоут при збереженні тесту → GC орфанів (`mindfly:gc-uploads`).
- Симуляційні ДЗ: `POST attempts` з `userAnswers2` зберігає дві зв'язані половини;
  `GET homeworks/{code}/results` віддає `{ part1, part2 }`.
- Міграції під усю схему (Laravel Blueprint, `hasTable`-guarded).
- Тестовий набір (unit + feature).
- Прибрано самописні хелпери (`LegacyCache`, `legacy_redis`) → `Cache` / `Redis`
  фасади. Кеш v2 має власний неймспейс (`CACHE_PREFIX=mfapi2`).

### Беклог (фаза 3)

- Мова відповідей мейлера: шаблони скопійовано з легасі як є (`resources/mail/`) —
  за потреби перевести на Mailable + Blade.
- Rate-limit тюнінг по групах (зараз єдиний `120/хв`).
- E2E-прогін на стейджі з реальною БД перед перемиканням клієнтів.

### Свідомо видалено

`adm.php`, `test.php`, `test-headers.php`, `sendTestMessage.php`,
`sitemap.xml.php`, `study/getTestGen.php`, `study/idToSlug.php`,
`accounts/syncOffline.php`, легасі-формат `getStats` v1. Причини — у
`SECURITY.md` та `CHANGELOG_FRONTEND.md`.

## Структура

```
app/
  Http/
    Controllers/   тонкі: валідація + виклик сервісу + ApiResponse
    Middleware/    AuthenticateToken, ResolveOptionalUser, EnsureRole, VerifyInternalSecret
    Requests/      EditorTestRequest (сувора схема дерева питань)
    Resources/     ProfileResource
  Services/        уся бізнес-логіка (Grading, Test, AnswerKey, Homework, Subscription, ...)
  Support/         ApiResponse, ApiError (enum кодів), LegacyCache (спільний Redis)
  Console/Commands/ cron-воркери
config/mindfly.php  бізнес-константи (ціни, коефіцієнти балів, шляхи до asset-ів)
routes/api.php      уся поверхня v2
```
"# mindfly-api2" 
