Andrey Epifancev 58d8891d06 spec(web-modules): веб под модули — состояние модулей, «Обзор», адреса модулей
Change web-modules по аудиту веба 24.10.2026 (#98).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 15:30:12 +04:00

8.7 KiB
Raw Blame History

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