Задача Егора 2ч.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Журнал исправления проблем
|
||||
|
||||
Обновлено: 2026-07-15 (Europe/Moscow).
|
||||
Обновлено: 2026-07-17 (Europe/Moscow).
|
||||
|
||||
## Область и неизменяемые данные
|
||||
|
||||
@@ -56,9 +56,9 @@
|
||||
| 8 | исправлено и проверено | `saveGrid` удалял данные до полной валидации и ловил исключение внутри transactional controller-метода, поэтому Spring коммитил delete и уже обработанные строки. | `AcademicCalendarGridService` полностью валидирует и строит replacement до delete, атомарно заменяет строки и очищает кэш через `afterCommit`; контроллер стал HTTP-адаптером. | 8 unit + 1 PostgreSQL proxy test; fingerprint старой сетки после отказа неизменен; полный backend 111/0/0/0. |
|
||||
| 9 | исправлено и проверено | При update старый pool закрывался до создания нового; Hikari допускал ленивый нерабочий pool, Flyway проглатывал ошибку, а отказ persistence мог сочетаться с изменённым локальным состоянием. | `TenantLifecycleService` выполняет prepare → connection validation → Flyway → Secret persistence → atomic snapshot swap; candidate закрывается при отказе, прежний route сохраняется, Secret компенсируется, старый pool закрывается после drain. | 18 целевых unit/MockMvc/PostgreSQL tests; create/update, credentials, invalid connection, Flyway, persistence, swap, HTTP `503` и in-flight connection. Полный backend: 132/0/0/0. |
|
||||
| 10 | требуется внешнее действие | Два pod перезаписывали целый tenant-документ без `resourceVersion`; TCP probes не отражали готовность tenant-БД. | Backend применяет доменные upsert/remove через GET → условный PUT по `resourceVersion`, ограниченный retry и безопасную компенсацию; Actuator разделяет process-only liveness и readiness обязательных tenant-БД. Оператору остаются RBAC `get/update`, mount без `subPath`, `TENANTS_CONFIG_REQUIRED=true` и HTTP probes в отсутствующем `../k8s`. | 64 целевых теста; полный backend 169/0/0/0; LF-хеши V1–V4 совпадают. Kustomize не выполнен: `../k8s` отсутствует. |
|
||||
| 11 | не начато | Sunday вычисляется повторным мутированием даты; ошибки запросов превращаются в пустые результаты и зелёный статус. | Чистая local-date утилита и три явных состояния проверки. | 2026-07-02, переход года, частичная/полная ошибка API. |
|
||||
| 11 | исправлено и проверено | Воскресенье вычислялось повторным мутированием одной даты, date-only строился через UTC, а ошибки кафедральных запросов превращались в пустой успешный результат. | `dashboard-conflicts.js` формирует локальный диапазон на отдельных объектах, загружает кафедры через `Promise.allSettled` и fail-closed различает `COMPLETE`/`PARTIAL`/`NOT_RUN`; зелёная карточка разрешена только для полного результата без конфликтов. | 12 `node:test`: 02.07.2026 00:15 Europe/Moscow, переход года, полный/частичный/нулевой успех и строгий UI-контракт; `npm run check` и синтаксис 25 JS/MJS-файлов — успешно. |
|
||||
| 12 | не начато | Access JWT хранится в Web Storage; origin исполняет удалённые модули; CSP допускает несовместимые сценарии. | Access JWT только в памяти, восстановление через HttpOnly refresh-cookie; локальные pinned assets; строгий CSP и удаление inline JS. | Login/refresh/logout/reload tests; статический аудит CSP/storage/imports. |
|
||||
| 13 | не начато | Watcher сравнивает только домены и фиксирует hash до успешной полной синхронизации. | Нормализованное сравнение полного `TenantConfig`, hash после успеха, bounded backoff. | Изменение URL/credentials и retry после ошибки. |
|
||||
| 13 | исправлено и проверено | Watcher сравнивал только домены, подтверждал hash до полного успеха, а API после мутации мог принять запаздывающую mounted-проекцию за актуальную. | Полное нормализованное сравнение, prepare/verify/Flyway/atomic swap без повторной записи Secret, hash-after-success, retry 30–300 секунд и semantic snapshot fence по `TenantSecretUpdateReceipt`. | 53 целевых теста, 2 PostgreSQL lifecycle-теста; полный backend 200/0/0/0. |
|
||||
| 14 | исправлено и проверено | Kubernetes HTTP-клиент имел trust-all TLS fallback. | `KubernetesTenantSecretUpdater` загружает service-account CA, строит PKIX trust store, включает HTTPS hostname verification и не имеет небезопасного fallback. | Mock HTTPS: доверенный CA принят; чужой CA, неверный hostname и отсутствующий CA отклонены. Backend 45/0/0. |
|
||||
| 15 | не начато | Не проверяются `start < end` и пересечения; duration доверяется клиенту; используемый DEFAULT scope можно изменить. | Централизованный валидатор, backend duration, DB/concurrency protection. | Overlap/adjacent/scope/concurrency tests. |
|
||||
| 16 | не начато | Create/update проверяют лишь порядок дат, но не пересечения и принадлежность семестра году. | Единый calendar validator и PostgreSQL range/exclusion constraints. | Create/update + конкурентные PostgreSQL tests. |
|
||||
@@ -324,8 +324,8 @@
|
||||
маскирует отказ. Actuator endpoints исключены из tenant-interceptor и не раскрывают
|
||||
components, домены, JDBC URL или credentials.
|
||||
- AutoUpdateDocs синхронизировал `docs/API.md`, `docs/ARCHITECTURE.md` и
|
||||
`docs/INFRASTRUCTURE.md`. Ограничение watcher по обновлению URL/credentials существующего
|
||||
домена явно оставлено проблеме № 13.
|
||||
`docs/INFRASTRUCTURE.md`. На этом этапе ограничение watcher по обновлению URL/credentials
|
||||
существующего домена было оставлено проблеме № 13 и устранено следующим этапом.
|
||||
- В текущей рабочей копии отсутствует внешний каталог `../k8s`, поэтому манифесты не менялись
|
||||
и Kustomize не запускался. Для production обязательны directory mount без `subPath`,
|
||||
`TENANTS_CONFIG_REQUIRED=true`, Role `get/update` для `tenants-secret` и HTTP liveness /
|
||||
@@ -343,18 +343,92 @@
|
||||
| Повторные LF-нормализованные SHA-256 V1/V2/V3/V4 | Совпадают с ранее зафиксированными значениями. |
|
||||
| `kubectl kustomize ../k8s` | Не выполнено: внешний каталог `../k8s` отсутствует в текущем workspace. |
|
||||
|
||||
### № 13 — полная и повторяемая синхронизация tenant-конфигурации
|
||||
|
||||
- `TenantLifecycleService.synchronizeFromPersistedConfig()` валидирует весь входной список,
|
||||
отклоняет нормализованные дубли до изменения runtime/readiness и сравнивает `name`, `domain`,
|
||||
`url`, `username`, `password`. Новый или изменённый tenant проходит candidate prepare,
|
||||
проверку соединения, Flyway и атомарный swap; persisted-путь не вызывает `TenantConfigStore`.
|
||||
- Hash mounted `tenants.json` становится применённым только после успешного разбора и полной
|
||||
lifecycle-синхронизации. Ошибка сохраняет прежний hash и повторяется с экспоненциальной
|
||||
задержкой 30–300 секунд; новая ревизия обходит backoff. Required missing/read/empty также
|
||||
наблюдаемы через readiness и русский лог без credentials, hash или fingerprint.
|
||||
- Перед `POST`/`DELETE` watcher под общим reentrant monitor применяет ещё не обработанный
|
||||
semantic baseline. Ошибка чтения/разбора/sync возвращает безопасный `503` до начала мутации.
|
||||
`TenantLifecycleMutationResult` возвращает controller-у immutable-квитанцию персистенции,
|
||||
но не раскрывает её в HTTP.
|
||||
- Snapshot fence ставится только для реально сохранённой изменённой версии
|
||||
(`persisted && changed`) и сравнивает нормализованные `previous/committed` snapshots, а не
|
||||
сырые JSON-хеши. Поэтому H0/H1 не откатывают две быстрые мутации до H2, форматирование JSON
|
||||
не влияет на решение, merged snapshot другого pod применяется, а local/no-op receipt ничего
|
||||
не блокирует.
|
||||
- Ошибка публикации readiness после успешного swap больше не помечает активный новый pool как
|
||||
`FAILED`: повтор проверяет действующее соединение и восстанавливает readiness без второго
|
||||
Flyway/swap. Старый pool передаётся на drain сразу после успешной атомарной публикации.
|
||||
- PostgreSQL 16.3/Testcontainers подтверждает сохранность прежнего route при неверных
|
||||
credentials и Flyway checksum, успешную замену на валидную БД, работу in-flight соединения,
|
||||
drain старого pool и отсутствие повторной записи Secret в persisted-пути.
|
||||
- Windows/Docker portability TLS-теста стабилизирована явным `https://localhost:<port>`,
|
||||
совпадающим с SAN тестового сертификата; production hostname verification не ослаблялась.
|
||||
- AutoUpdateDocs повторно синхронизировал `docs/API.md`, `docs/ARCHITECTURE.md` и
|
||||
`docs/INFRASTRUCTURE.md` с финальной receipt/snapshot-схемой.
|
||||
|
||||
Фактические проверки этапа:
|
||||
|
||||
| Команда | Результат |
|
||||
|---|---|
|
||||
| `TenantConfigWatcherTest,TenantLifecycleServiceTest,DatabaseControllerTest,TenantRoutingDataSourceTest` | 53 теста, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
|
||||
| `TenantLifecyclePostgreSqlIntegrationTest` | 2 теста, 0 failures, 0 errors, 0 skipped; реальный PostgreSQL 16.3/Testcontainers. |
|
||||
| `KubernetesTenantSecretUpdaterTest` | 21 тест, 0 failures, 0 errors, 0 skipped; TLS, CAS, reconciliation и компенсация. |
|
||||
| Полный `mvn -Dapi.version=1.44 test` | 200 тестов, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
|
||||
| `git diff --check` | Успешно. |
|
||||
| Повторные LF-нормализованные SHA-256 V1/V2/V3/V4 | Совпадают с ранее зафиксированными значениями; миграционные файлы не изменялись. |
|
||||
|
||||
### № 11 — достоверный статус проверки конфликтов на дашборде
|
||||
|
||||
- `formatLocalDate()` формирует `YYYY-MM-DD` из локальных компонентов `Date` без
|
||||
`toISOString()`. `currentWeekDateRange()` создаёт отдельный понедельник и вычисляет
|
||||
воскресенье как `понедельник + 6 дней`, поэтому начало месяца и переход года не мутируют
|
||||
исходную дату повторно.
|
||||
- Расписания кафедр загружаются независимо через `Promise.allSettled`. Результат хранит число
|
||||
ответивших кафедр и одно из состояний: `COMPLETE`, `PARTIAL` или `NOT_RUN`; технические
|
||||
причины отказов не входят в UI-модель.
|
||||
- `conflictCheckPresentation()` проверяет диапазон, количество конфликтов и согласованность
|
||||
счётчиков fail-closed. Зелёная карточка разрешена только для `COMPLETE`, когда проверены все
|
||||
кафедры и конфликтов нет. `PARTIAL` всегда показывает предупреждение и не скрывает найденные
|
||||
по доступной части конфликты, а полный отказ отображается как «Проверка не выполнена».
|
||||
- Карточки используют русские безопасные тексты, экранирование, явные success/warning/error
|
||||
состояния, период и число проверенных кафедр. Контейнер получил live-region и `aria-busy`,
|
||||
кнопка — `type="button"`; после ошибки начальной загрузки «Перепроверить» повторно получает
|
||||
справочники, а не анализирует пустой список.
|
||||
- Тот же локальный formatter устранит сдвиг даты сегодняшнего расписания до 03:00 по Москве.
|
||||
Остальная часть общей timezone-проблемы остаётся в объёме № 29.
|
||||
- Добавлены dependency-free тесты на встроенном `node:test` и воспроизводимые npm-команды.
|
||||
AutoUpdateDocs синхронизировал структуру, контракт Red Zone и команды проверки в
|
||||
`docs/FRONTEND.md`.
|
||||
|
||||
Фактические проверки этапа:
|
||||
|
||||
| Команда | Результат |
|
||||
|---|---|
|
||||
| `npm test` в `frontend/` | 12 тестов, 12 passed, 0 failed; локальная полночь `2026-07-02 00:15 Europe/Moscow`, диапазон `2026-06-29…2026-07-05`, переход года и состояния отказов. |
|
||||
| `npm run check` в `frontend/` | Синтаксис `dashboard-conflicts.js` и `dashboard.js`, затем 12 тестов — успешно. |
|
||||
| Рекурсивный `node --check` для `frontend/**/*.js` и `frontend/**/*.mjs` | 25 файлов, ошибок нет. |
|
||||
| Полный backend-регресс | Backend на этапе № 11 не изменялся; последний полный прогон этапа № 13 остаётся зелёным: 200/0/0/0. |
|
||||
| `git diff --check` | Успешно. |
|
||||
| Защищённые файлы | `BUG_REPORT.md` и существующие Flyway-миграции не изменялись. |
|
||||
|
||||
## Точка продолжения
|
||||
|
||||
Текущий этап: **№ 13 — полная синхронизация изменённых tenant credentials/URL watcher-ом**.
|
||||
Текущий этап: **№ 12 — access JWT и удалённый JavaScript в одном origin**.
|
||||
|
||||
Следующая операция:
|
||||
|
||||
1. сравнивать полную нормализованную активную конфигурацию с persisted snapshot, а не только
|
||||
набор доменов;
|
||||
2. при изменении URL/username/password выполнять prepare → verify → Flyway → atomic swap без
|
||||
повторной записи Secret;
|
||||
3. считать hash применённым только после полной успешной синхронизации и не подтверждать
|
||||
произвольное содержимое mount через `refreshHash()` после API-мутации;
|
||||
4. добавить bounded retry/backoff и тесты изменения credentials, восстановления прежнего
|
||||
файла после ошибки и межподового merged snapshot;
|
||||
5. выполнить полный прогон и AutoUpdateDocs перед изменением статуса № 13.
|
||||
1. инвентаризировать `localStorage`/`sessionStorage`, refresh-cookie, удалённые `import()` и
|
||||
inline-скрипты во всех frontend entrypoints и HTTP/CSP-конфигурации;
|
||||
2. выбрать единый контракт access JWT только в памяти с восстановлением через HttpOnly
|
||||
refresh-cookie, не оставляя токен в Web Storage после login/refresh/logout/reload;
|
||||
3. локализовать и зафиксировать внешние runtime-зависимости, затем ввести CSP без
|
||||
`unsafe-inline`/`unsafe-eval` и устранить несовместимые inline-вставки;
|
||||
4. покрыть login/refresh/logout/reload тестами и выполнить статический аудит storage/imports/CSP
|
||||
перед AutoUpdateDocs и изменением статуса № 12.
|
||||
|
||||
Reference in New Issue
Block a user