Files
Learn_System/plans/sim-builder/phase-3-persistence-api.md
T

52 lines
3.5 KiB
Markdown

# Phase 3: БД + API (custom_sims)
**Status:** ⬜ Not Started
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** backend
## Objective
Сохранение custom-симуляций: таблица БД, CRUD API под авторизацией с проверкой владения,
серверная валидация спеки. После фазы спека сохраняется/грузится/удаляется через API.
## Tasks
- [ ] Миграция `backend/src/db/migrations/0NN_custom_sims.sql` (следующий свободный номер):
таблица `custom_sims` (id, owner_id FK users ON DELETE CASCADE, title, description, subject,
grade, cat, spec_json TEXT, status TEXT 'draft|published' DEFAULT 'draft', version INT,
created_at, updated_at) + индекс по owner_id, status.
- [ ] Контроллер `backend/src/controllers/customSimController.js`: list (own + published), get,
create, update, remove. Владение проверяется на mutate (owner или admin).
- [ ] Серверная валидация спеки `validateSpec(spec)`: размер JSON, `specVersion`, лимиты
(число params/objects, глубина строк-выражений), типы объектов из whitelist, строки-подписи
обрезаются/санитизируются. Отказ с 400 при нарушении. ⛔ Никакого исполнения спеки на сервере.
- [ ] Роуты `backend/src/routes/customSims.js`: `GET /api/custom-sims` (свои+published),
`GET /api/custom-sims/:id`, `POST /` (teacher/admin), `PUT /:id`, `DELETE /:id`. Смонтировать в server.js под authMiddleware + фича-гейт при необходимости.
- [ ] Клиент `js/api.js`: `customSimsList/Get/Create/Update/Delete` + добавить в `window.LS`.
- [ ] Тесты `backend/tests/custom-sims.test.js`: CRUD, ownership (403 чужой), валидация (400 кривая спека). Использовать `seedRow()` паттерн (устойчив к дрейфу схемы).
## Files to Modify/Create
- `backend/src/db/migrations/0NN_custom_sims.sql` (new)
- `backend/src/controllers/customSimController.js` (new)
- `backend/src/routes/customSims.js` (new)
- `backend/src/server.js` — монтирование роутера (modify)
- `js/api.js` — клиентские методы (modify)
- `backend/tests/custom-sims.test.js` (new)
## Acceptance Criteria
- POST сохраняет спеку, GET возвращает свои+published, PUT/DELETE только владельцу/админу (403 иначе).
- Кривая/огромная спека → 400. Тесты зелёные (в пределах baseline).
- `npm run migrate` применяет таблицу; роут не отдаёт SPA-fallback после рестарта.
## Notes
- node:sqlite (DatabaseSync), НЕ better-sqlite3.
- Спека хранится как TEXT(JSON); парс/валидация — на входе.
- Не делать blanket `router.use(requireRole('admin'))` — read-роуты auth-only, мутации — inline requireRole.
## Review Checklist
- [ ] Все задачи выполнены
- [ ] Ownership и валидация спеки покрыты тестами
- [ ] Миграция идемпотентна (IF NOT EXISTS / INSERT OR IGNORE где надо)
- [ ] `npm run lint:routes` чисто; тесты в пределах baseline
## Handoff to Next Phase
<!-- Заполняет реализатор -->