Change web-modules по аудиту веба 24.10.2026 (#98). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
113 lines
8.7 KiB
Markdown
113 lines
8.7 KiB
Markdown
## MODIFIED Requirements
|
||
|
||
### Requirement: Раздача веб-приложения
|
||
Хаб SHALL отдавать собранное веб-приложение (спека `web-app`) из каталога пакета `src/sdlc_hub/web/app/`, читая файлы
|
||
с диска на каждый запрос:
|
||
- `GET /app/assets/<путь>` — файл из `assets/` сборки; путь, который после разрешения выходит за `assets/`, не
|
||
существует или содержит нулевой байт, — 404; `Content-Type` по расширению (`.js` — `text/javascript; charset=utf-8`, `.css` —
|
||
`text/css; charset=utf-8`, `.svg` — `image/svg+xml`, `.png` — `image/png`, `.ico` — `image/x-icon`, `.woff2` —
|
||
`font/woff2`, `.map` — `application/json`, прочее — `application/octet-stream`);
|
||
`Cache-Control: public, max-age=31536000, immutable`;
|
||
- любой другой `GET /app` или `GET /app/…` — `index.html` сборки, `text/html; charset=utf-8`, `Cache-Control: no-cache`;
|
||
- если `index.html` сборки нет — на любой `GET /app…` 503 `text/html; charset=utf-8` с текстом, что приложение не
|
||
собрано и нужно выполнить `make web` в каталоге sdlc-hub;
|
||
- `GET /` и `GET /links` — 302 с `Location: /app/` (главная страница приложения — «Обзор»).
|
||
Сборка приложения не хранится в git; её создаёт `make web`.
|
||
|
||
#### Scenario: Файл сборки
|
||
- **WHEN** в сборке есть `assets/index-abc123.js`, запрошен `GET /app/assets/index-abc123.js`
|
||
- **THEN** 200, `Content-Type: text/javascript; charset=utf-8`, `Cache-Control` с `immutable`, тело — содержимое файла
|
||
|
||
#### Scenario: Путь приложения
|
||
- **WHEN** запрошен `GET /app/issues/AURORA-1620`
|
||
- **THEN** 200, тело — `index.html` сборки, `Cache-Control: no-cache`
|
||
|
||
#### Scenario: Выход за каталог сборки
|
||
- **WHEN** запрошен `GET /app/assets/../../api.py` (в том числе в виде `%2e%2e`)
|
||
- **THEN** 404, содержимое файлов вне `assets/` не отдаётся
|
||
|
||
#### Scenario: Приложение не собрано
|
||
- **WHEN** каталога сборки нет, запрошен `GET /app/board`
|
||
- **THEN** 503, тело — HTML с подсказкой `make web`
|
||
|
||
#### Scenario: Старый адрес доски
|
||
- **WHEN** запрошен `GET /links` или `GET /`
|
||
- **THEN** 302, `Location: /app/`
|
||
|
||
#### Scenario: Нулевой байт в пути
|
||
- **WHEN** запрошен `GET /app/assets/%00` или `GET /app/assets/a%00.js`
|
||
- **THEN** 404 `not_found`, а не 500
|
||
|
||
### Requirement: Маршруты проектов
|
||
Хаб SHALL предоставлять под клиентским токеном:
|
||
- `GET /projects` → список проектов по `created_at`, затем `label`; каждый — плоский объект из полей ядра (`label`,
|
||
`name`, `created_at`) и полей всех модулей (`modules`, «Поля проекта от модулей»). Модуль jira даёт `board_id`,
|
||
`board_type`, `issues_count` (задачи зеркала с меткой проекта), `jira_synced_at` (время завершения последнего полного
|
||
обновления зеркала, общее для всех проектов; `null`, если его не было), модуль wiki — `wiki_root`, `wiki_root_title`,
|
||
`wiki_pages`, `wiki_synced_at`, `wiki_sync_error`.
|
||
- `POST /projects` `{label, name, author?, <поля модулей>}` → 201 `{project, batch_id, batches}`: в одной транзакции
|
||
проект записывается, модули записывают свои поля и ставят свои пакеты; `batches` — список `{module, id}` всех
|
||
поставленных пакетов в порядке модулей; `batch_id` — пакет первого модуля, вернувшего пакет (у jira — «Новый проект <метка>» из одного `refresh` по базовой JQL, уже включающей новую метку). Автор пакетов —
|
||
`author` (по умолчанию `api`). Неизвестное поле, неверные поля ядра (см. `data-model`, «Проекты») или модуля — 422 с
|
||
указанием поля; метка, совпадающая с существующей без учёта регистра, — 409. При ошибке ни проекта, ни пакетов нет.
|
||
- `PATCH /projects/{label}` `{<изменяемые поля модулей>, author?}` → 200 `{project, batch_ids, batches}`: модули правят
|
||
свои поля и ставят пакеты в одной транзакции; `batches` — список `{module, id}`. Неизменяемое поле (метка, название, `board_id`) — 422 «<поле> после создания не
|
||
меняется»; неизвестное поле или неверное значение — 422 с указанием поля; проекта нет — 404.
|
||
|
||
#### Scenario: Новый проект
|
||
- **WHEN** `POST /projects {label: "STOCKS_DRRK", name: "Запасы ДРРК", board_id: 5120, author: "viewer"}`, есть проект `AWPCA`
|
||
- **THEN** 201; `GET /projects` отдаёт два проекта, у `STOCKS_DRRK` `board_type = null`, `wiki_root = null`; в очереди
|
||
пакет автора `viewer` с `refresh {jql: 'project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")'}`
|
||
|
||
#### Scenario: Проект без доски
|
||
- **WHEN** `POST /projects {label: "STOCKS_DRRK", name: "Запасы ДРРК"}`
|
||
- **THEN** 201, `board_id = null`
|
||
|
||
#### Scenario: Проект с корнем вики
|
||
- **WHEN** `POST /projects {label: "STOCKS_DRRK", name: "Запасы ДРРК", wiki_root: "DOC-001234"}`
|
||
- **THEN** 201; в очереди пакет `refresh` и пакет `wiki_sync {label: "STOCKS_DRRK", root: "DOC-001234"}`; `batch_id` —
|
||
пакет `refresh`; `batches` — `[{module: "jira", id: <refresh>}, {module: "wiki", id: <wiki_sync>}]`
|
||
|
||
#### Scenario: Повтор метки
|
||
- **WHEN** `POST /projects {label: "AWPCA", name: "…"}`, проект `AWPCA` есть
|
||
- **THEN** 409, пакет не поставлен
|
||
|
||
#### Scenario: Без названия
|
||
- **WHEN** `POST /projects {label: "STOCKS_DRRK", name: " "}`
|
||
- **THEN** 422 с указанием поля `name`
|
||
|
||
#### Scenario: Неизвестное поле
|
||
- **WHEN** `POST /projects {label: "X", name: "X", board: 5}`
|
||
- **THEN** 422 с указанием поля `board`, проекта нет
|
||
|
||
#### Scenario: Корень вики у существующего проекта
|
||
- **WHEN** `PATCH /projects/AWPCA {wiki_root: "DOC-000777"}`
|
||
- **THEN** 200, `batch_ids` — один пакет `wiki_sync`, `batches` — `[{module: "wiki", id: <он же>}]`; `GET /projects` отдаёт `AWPCA` с `wiki_root = DOC-000777`
|
||
|
||
#### Scenario: Доску не меняют
|
||
- **WHEN** `PATCH /projects/AWPCA {board_id: 1}`
|
||
- **THEN** 422 «board_id после создания не меняется», доска прежняя
|
||
|
||
#### Scenario: Свежесть Jira в проекте
|
||
- **WHEN** последний полный пакет `refresh` завершён в 11:07, `GET /projects`
|
||
- **THEN** у каждого проекта `jira_synced_at` — это время; до первого полного обновления — `null`
|
||
|
||
## ADDED Requirements
|
||
|
||
### Requirement: Модуль пакета
|
||
`GET /batches` и `GET /batches/{id}` SHALL отдавать у пакета поле `module` — имя модуля, которому принадлежит тип первой
|
||
команды пакета (по описаниям модулей в реестре); пакет без команд или с типом, которого не знает ни один модуль, —
|
||
`module = null`. Фильтр `GET /batches?module=<имя>` SHALL отдавать только пакеты модуля; неизвестный модуль — 404.
|
||
|
||
#### Scenario: Модуль у пакетов
|
||
- **WHEN** в очереди пакет «Автообновление» (`refresh`, автор `hub`) и пакет `wiki_sync` проекта `AWPCA`; `GET /batches`
|
||
- **THEN** у первого `module = "jira"`, у второго `module = "wiki"`
|
||
|
||
#### Scenario: Фильтр по модулю
|
||
- **WHEN** `GET /batches?module=wiki&project=AWPCA`
|
||
- **THEN** только пакеты модуля wiki проекта `AWPCA`
|
||
|
||
#### Scenario: Неизвестный модуль
|
||
- **WHEN** `GET /batches?module=nope`
|
||
- **THEN** 404
|