This commit is contained in:
2026-10-10 15:58:44 +07:00
parent 3efdb52c3c
commit e5191ebe50
9 changed files with 204 additions and 13 deletions
+1
View File
@@ -1,3 +1,4 @@
email_server email_server
evening_detective_server evening_detective_server
evening_detective_frontend evening_detective_frontend
/pass.txt
+27 -9
View File
@@ -166,16 +166,19 @@ python3 tests/venue/smoke.py --verify
openssl x509 -in venue/certificates/fullchain.pem -noout -dates openssl x509 -in venue/certificates/fullchain.pem -noout -dates
``` ```
4. На Cudy вручную закрепите IP ноутбука и настройте локальный DNS: оба домена 4. На Cudy вручную закрепите IP ноутбука в **Advanced Settings → IP/MAC Binding**.
должны указывать на этот IP. DHCP должен выдавать клиентам адрес локального На TR3000 с прошивкой 2.5.27 DE в прочитанных разделах Custom DNS и DHCP нет
DNS. Возможность такой настройки проверяется на конкретной модели и прошивке; формы статических записей. Поэтому venue запускает отдельный CoreDNS на ноутбуке.
выбор внешнего «Custom DNS» сам по себе не создаёт локальные записи. В **Advanced Settings → DHCP Server** задайте **Preferred DNS = IP ноутбука**,
Проверьте клиентскую изоляцию, DNS rebind protection и IPv6: клиенты должны **Alternate DNS** оставьте пустым. Переподключите телефоны к Wi-Fi, чтобы они
достигать ноутбука, а AAAA-записи не должны уводить их на production. получили новые параметры DHCP. Custom DNS роутера менять не требуется.
При отсутствии нужных возможностей прошивки остановите настройку и согласуйте Проверьте клиентскую изоляцию и настройки Private DNS/DoH устройств: запросы
отдельное решение; скрипты проекта не меняют роутер и не прошивают его. двух доменов должны приходить на локальный DNS, а клиенты — достигать ноутбука.
Скрипты проекта не меняют роутер и не прошивают его.
5. В `venue/.env` задайте `VENUE_BIND_IP=<постоянный-IP-ноутбука>` и разрешите 5. В `venue/.env` задайте `VENUE_BIND_IP=<постоянный-IP-ноутбука>` и разрешите
входящие TCP 80/443 из LAN в межсетевом экране ноутбука. Порты должны быть свободны. входящие TCP 80/443 и TCP/UDP 53 из LAN в межсетевом экране ноутбука.
`VENUE_BIND_IP` должен быть конкретным IPv4-адресом ноутбука, не `0.0.0.0`:
он используется и для публикации портов, и для DNS-ответов. Порты должны быть свободны.
Для HTTPS менять `APP_BASE_URL` в `.env` не нужно: Compose-дополнение задаёт Для HTTPS менять `APP_BASE_URL` в `.env` не нужно: Compose-дополнение задаёт
прежний HTTPS-домен интерфейса для QR-ссылок и материалов. прежний HTTPS-домен интерфейса для QR-ссылок и материалов.
6. Запустите: 6. Запустите:
@@ -195,6 +198,21 @@ python3 tests/venue/smoke.py --verify
прежний API-домен также обслуживается локально. HTTP на портах 80 и прежнем прежний API-домен также обслуживается локально. HTTP на портах 80 и прежнем
`VENUE_PORT` перенаправляется на HTTPS с сохранением пути и параметров. `VENUE_PORT` перенаправляется на HTTPS с сохранением пути и параметров.
CoreDNS публикуется только на `VENUE_BIND_IP:53` по UDP/TCP. Оба домена получают
этот IPv4 локально; AAAA-запросы не получают внешние адреса. Для них интернет
не требуется. Остальные домены пересылаются на 1.1.1.1/8.8.8.8 и работают при
доступном интернете; их недоступность без WAN не мешает локальным доменам.
DNS подключён к сети публикации портов и не имеет доступа к внутренней сети
backend/БД. Образ CoreDNS скачивается при первой подготовке и используется
повторно без скачивания. При изменении IP ноутбука обновите `VENUE_BIND_IP`,
настройки DHCP Cudy и повторите `make venue`.
Проверка DNS до настройки клиентов:
```sh
python3 tests/venue/dns.py --server 192.168.10.155
```
После продления сертификата с доступным интернетом замените оба PEM-файла и После продления сертификата с доступным интернетом замените оба PEM-файла и
повторите `make venue`: Nginx проверяет конфигурацию и перечитывает сертификат. повторите `make venue`: Nginx проверяет конфигурацию и перечитывает сертификат.
Продление не выполняется автоматически. После обновления кода выполните Продление не выполняется автоматически. После обновления кода выполните
+12 -3
View File
@@ -27,11 +27,11 @@
### ED-029 — Автономная площадка и панель организатора ### ED-029 — Автономная площадка и панель организатора
- Статус: `BLOCKED` — подготовка этапа 3 в репозитории завершена; приёмка требует параметров и доступа к Cudy, отдельного доверенного сертификата и проверки Android/iPhone без WAN. - Статус: `BLOCKED` — HTTPS, сертификат, локальный DNS и привязка IP ноутбука готовы; выдача DNS через DHCP Cudy настроена; требуется проверить Android/iPhone без WAN.
- Репозитории: корневой, `evening_detective_server`, `evening_detective_frontend`. - Репозитории: корневой, `evening_detective_server`, `evening_detective_frontend`.
- Цель: реализовать принятый пользователем план из 15 последовательных этапов: автономное окружение; локальные организаторы; прежние адреса через Cudy; пакет игры; экспорт с блокировкой; локальный импорт; интерфейс передачи; полный билетный и игровой flow; пакет результатов; возврат онлайн; интерфейс возврата; устройства; панель состояния; сбор данных Cudy; Wi-Fi и испытание площадки. - Цель: реализовать принятый пользователем план из 15 последовательных этапов: автономное окружение; локальные организаторы; прежние адреса через Cudy; пакет игры; экспорт с блокировкой; локальный импорт; интерфейс передачи; полный билетный и игровой flow; пакет результатов; возврат онлайн; интерфейс возврата; устройства; панель состояния; сбор данных Cudy; Wi-Fi и испытание площадки.
- Критерии: проверяемый результат каждого этапа; цикл «онлайн → ноутбук → проведение → онлайн» через интерфейс с сохранением QR, билетов и связей; повторные импорты без дублей; мониторинг устройств и Wi-Fi; полный прогон без WAN, 100 клиентов и проверка покрытия. - Критерии: проверяемый результат каждого этапа; цикл «онлайн → ноутбук → проведение → онлайн» через интерфейс с сохранением QR, билетов и связей; повторные импорты без дублей; мониторинг устройств и Wi-Fi; полный прогон без WAN, 100 клиентов и проверка покрытия.
- Ограничения: только тестовые игры до завершения возврата; существующие правила игры и формат `offline` сохраняются; без новых зависимостей и отклонений от стандартов; без персональных списков игроков, CPU/памяти Cudy, записи настроек роутера и автоматического переключения серверов; секреты и пакеты вне Git; commit/push выполняет пользователь. - Ограничения: только тестовые игры до завершения возврата; существующие правила игры и формат `offline` сохраняются; без несогласованных новых зависимостей и отклонений от стандартов; без персональных списков игроков, CPU/памяти Cudy и автоматического переключения серверов; настройки роутера менять только по явному запросу пользователя; секреты и пакеты вне Git; commit/push выполняет пользователь.
- Проверка: Go-стандарт и `go test ./...`; PostgreSQL-интеграционные тесты для атомарности; frontend-стандарт, type-check, lint, lint:css и build; штатная генерация protobuf; ручные контрольные точки после этапов 3, 8, 11, 15. - Проверка: Go-стандарт и `go test ./...`; PostgreSQL-интеграционные тесты для атомарности; frontend-стандарт, type-check, lint, lint:css и build; штатная генерация protobuf; ручные контрольные точки после этапов 3, 8, 11, 15.
- Прогресс: этапы 1–2 реализованы и проверены в локальном Docker-окружении. Добавлены отдельный Compose, подготовка образов заранее, постоянные тома, миграции перед стартом, healthchecks и прокси. Явный `APP_MODE=venue` отключает SMTP, фоновые письма и внешний webhook; почтовые операции аккаунта возвращают безопасную ошибку до изменения данных. Локальные аккаунты создаются штатным `dev-user.sql`, роли и правила автоматического подтверждения команд сохраняются. Подготовка этапа 3 реализована; его аппаратная приёмка и этапы 4–15 ещё не завершены. - Прогресс: этапы 1–2 реализованы и проверены в локальном Docker-окружении. Добавлены отдельный Compose, подготовка образов заранее, постоянные тома, миграции перед стартом, healthchecks и прокси. Явный `APP_MODE=venue` отключает SMTP, фоновые письма и внешний webhook; почтовые операции аккаунта возвращают безопасную ошибку до изменения данных. Локальные аккаунты создаются штатным `dev-user.sql`, роли и правила автоматического подтверждения команд сохраняются. Подготовка этапа 3 реализована; его аппаратная приёмка и этапы 4–15 ещё не завершены.
- Дополнение по запросу пользователя: структура `venue` приведена к `prod` (`backend/`, `frontend/dist/`, `migrations/`, `Dockerfile.backend`, `docker-compose.yml`, `deploy.sh`); имена основных Compose-сервисов совпадают. Общая генерация `make build` / `make prod-build` готовит артефакты обеих папок; frontend собирается отдельно с настройками своего окружения. Текущие файлы `prod` при этой работе не изменяются; сборка проверяется с production-выводом в `/tmp`. - Дополнение по запросу пользователя: структура `venue` приведена к `prod` (`backend/`, `frontend/dist/`, `migrations/`, `Dockerfile.backend`, `docker-compose.yml`, `deploy.sh`); имена основных Compose-сервисов совпадают. Общая генерация `make build` / `make prod-build` готовит артефакты обеих папок; frontend собирается отдельно с настройками своего окружения. Текущие файлы `prod` при этой работе не изменяются; сборка проверяется с production-выводом в `/tmp`.
@@ -42,11 +42,20 @@
- Проверка дополнения завершена: `make build PROD_DIR=/tmp/evening-artifacts-check` прошёл, backend и миграции в обоих наборах совпадают; type-check и две frontend-сборки прошли. Проверены Compose config, shell-синтаксис и diff. Local собран и запущен через новый `deploy.sh`; API-smoke и Firefox подтвердили вход, тестовую игру, подтверждённую команду и сохранность материала. Контрольные суммы файлов `prod` до и после работы совпадают. Проверка production-запуска не выполнялась; существующие файлы и конфигурация `prod` не изменены. - Проверка дополнения завершена: `make build PROD_DIR=/tmp/evening-artifacts-check` прошёл, backend и миграции в обоих наборах совпадают; type-check и две frontend-сборки прошли. Проверены Compose config, shell-синтаксис и diff. Local собран и запущен через новый `deploy.sh`; API-smoke и Firefox подтвердили вход, тестовую игру, подтверждённую команду и сохранность материала. Контрольные суммы файлов `prod` до и после работы совпадают. Проверка production-запуска не выполнялась; существующие файлы и конфигурация `prod` не изменены.
- Результат проверки этапов 1–2: ручное ревью diff по Go- и frontend-стандартам выполнено, отклонений нет; `gofmt`, `go test ./...`, frontend `type-check`, `lint`, `lint:css`, сборка и полный `sh venue/prepare.sh` прошли. Compose config и shell-синтаксис проверены. Smoke через API создал тестовую игру, подтверждённую команду и материал; после `down` / `up --no-build --pull never` всё сохранилось. Firefox без окна подтвердил реальный вход, экран игры и команду после перезапуска. Приложение не имеет внешнего маршрута сети. Повторная инициализация не перезаписывает секреты. Инструкции запуска и повторяемый API-smoke находятся в корневом README и `tests/venue/smoke.py`. Проверка телефонов при отключённом WAN ещё не выполнена. - Результат проверки этапов 1–2: ручное ревью diff по Go- и frontend-стандартам выполнено, отклонений нет; `gofmt`, `go test ./...`, frontend `type-check`, `lint`, `lint:css`, сборка и полный `sh venue/prepare.sh` прошли. Compose config и shell-синтаксис проверены. Smoke через API создал тестовую игру, подтверждённую команду и материал; после `down` / `up --no-build --pull never` всё сохранилось. Firefox без окна подтвердил реальный вход, экран игры и команду после перезапуска. Приложение не имеет внешнего маршрута сети. Повторная инициализация не перезаписывает секреты. Инструкции запуска и повторяемый API-smoke находятся в корневом README и `tests/venue/smoke.py`. Проверка телефонов при отключённом WAN ещё не выполнена.
- Внешние данные: запрошены модель/IP Cudy, постоянный IP ноутбука, доступ к проверенной прошивке и возможность DNS-проверки для сертификатов. Проверки телефонов, 100 клиентов и покрытия требуют оборудования. - Внешние данные: запрошены модель/IP Cudy, постоянный IP ноутбука, доступ к проверенной прошивке и возможность DNS-проверки для сертификатов. Проверки телефонов, 100 клиентов и покрытия требуют оборудования.
- Привязка ноутбука на Cudy по явному запросу «закрепи сам»: перед изменением подтверждены текущие IP 192.168.10.155 и MAC BC:D2:2C:29:4A:96. Через штатную форму IP/MAC Binding добавлена запись `Venue laptop`, выполнен Save & Apply; повторное чтение подтвердило ровно одну новую запись с нужной парой IP/MAC и сохранность обеих существующих привязок. DHCP/DNS-настройки и прошивка не менялись. Проверка DNS venue по UDP/TCP после сохранения прошла. Принудительное переподключение ноутбука для проверки новой DHCP-аренды не выполнялось; проверка телефонов без WAN остаётся обязательной.
- Локальный DNS по согласованию пользователя добавлен: CoreDNS 1.14.7 запускается с venue на TCP/UDP 53 только на `VENUE_BIND_IP`; оба домена возвращают этот IPv4, AAAA-запросы не получают внешних адресов. Отдельные локальные зоны не пересылаются upstream; остальные домены направляются в 1.1.1.1/8.8.8.8 при доступном WAN. DNS подключён только к сети публикации портов и не имеет доступа к внутренней сети приложения/БД. Образ закреплён по версии, скачивается при отсутствии и используется повторно; новая инфраструктурная зависимость явно согласована пользователем. Локальная `.env` изменена только в `VENUE_BIND_IP=192.168.10.155`, секреты сохранены. `make venue` успешно пересобрал образ из готовых файлов, выполнил миграции и перезапустил HTTPS/DNS с сохранением томов. Настройки Cudy не менялись.
- Проверки DNS: `python3 tests/venue/deploy.py`, `python3 tests/venue/https.py --docker`, `python3 tests/venue/dns.py --docker`, `python3 tests/venue/dns.py --server 192.168.10.155`, shell-/Python-синтаксис и `git diff --check` прошли. Реальный CoreDNS в отдельной внутренней Docker-сети подтвердил локальные ответы по UDP/TCP и отсутствие внешних IPv6-ответов без WAN до/после перезапуска. Проверены Compose mount/порты/DNS-IP и изоляция backend; обычный внешний домен успешно разрешается через upstream на запущенной площадке. HTTPS-smoke с принудительным разрешением обоих имён только в IP ноутбука и обычной проверкой TLS подтвердил вход организатора, сохранность тестовой игры/команды/материала, SPA и отключённые email-операции. Ручное ревью diff выполнено; код Go/Vue и protobuf не менялся. Проверка DNS через DHCP Cudy и телефонов без WAN ещё требуется; IP ноутбука пока динамический.
- Проверка реального Cudy: TR3000, прошивка 2.5.27 DE, панель доступна на 192.168.10.1; текущий DHCP-IP ноутбука 192.168.10.155 ещё не закреплён. По предоставленному пользователем файлу выполнен вход и прочитаны Custom DNS и DHCP. В этих разделах нет формы статических записей «домен → IP»: Custom DNS выбирает upstream DNS, DHCP позволяет выдавать клиентам Preferred/Alternate DNS. Настройки роутера не менялись. Для сохранения штатной прошивки предложен отдельный локальный DNS на ноутбуке с двумя записями и выдачей его адреса через DHCP; это дополнение к прежнему плану требует согласования инфраструктуры. Пользовательский pass.txt защищён режимом 600 и исключён из Git; пароль и сессионные данные не выводились. Следующий шаг — согласовать реализацию локального DNS, закрепить IP, настроить DHCP и выполнить аппаратную приёмку.
- Выпуск сертификата по запросу пользователя начат: скачан официальный образ Certbot, отдельный ACME-аккаунт и запрос на два домена созданы в игнорируемом `venue/certificates/acme/` с ограниченным доступом. Certbot ожидает добавления двух TXT-записей DNS-01 в публичный DNS; доступ к DNS в среде отсутствует, значения переданы пользователю. Сертификат ещё не выдан и не установлен; production-ключи и настройки не использованы, площадка не перезапускалась. После появления записей проверить публичный DNS, завершить валидацию, установить полную цепочку и ключ в `venue/certificates/` и проверить их перед запуском.
- Результат выпуска сертификата: после добавления пользователем TXT-записей оба значения проверены через публичный DNS, DNS-01 успешно пройден. Отдельный сертификат Let's Encrypt для обоих прежних доменов установлен в `venue/certificates/fullchain.pem`, ключ — в `privkey.pem` с режимом 600; каталог и ACME-файлы вне Git, production-ключ не использован. Срок действия до 2027-01-08 06:53:26 UTC. OpenSSL подтвердил системное доверие полной цепочке, оба домена и соответствие ключа; игнорирование обоих файлов проверено. Автопродления нет: до истечения срока повторить DNS-01 и установить обновлённые PEM-файлы. Площадка в рамках выпуска сертификата не перезапускалась; Cudy и телефоны без WAN ещё не проверены.
- Актуальный запуск по последующему запросу пользователя: `make venue` всегда использует HTTPS-дополнение, выполняет `docker compose build --pull=false` из заранее подготовленных артефактов и `up --force-recreate --no-build --pull never`; тома данных сохраняются. `venue-update` и `venue-https` — алиасы единого запуска. Сертификат проверяется до Docker; Go/Vue и npm-зависимости при деплое не собираются. Отсутствующие runtime-образы и базовый Alpine скачиваются, имеющиеся не обновляются автоматически. HTTPS теперь используется smoke-проверкой по умолчанию. README и тесты приведены к единому поведению; `python3 tests/venue/deploy.py`, shell-синтаксис, Python-синтаксис, dry-run всех трёх Make-команд и `git diff --check` прошли; diff вручную проверен. Реальный перезапуск площадки с публичным сертификатом и сборка Docker-образа без WAN не выполнялись; Cudy и проверка телефонов остаются внешней контрольной точкой. - Актуальный запуск по последующему запросу пользователя: `make venue` всегда использует HTTPS-дополнение, выполняет `docker compose build --pull=false` из заранее подготовленных артефактов и `up --force-recreate --no-build --pull never`; тома данных сохраняются. `venue-update` и `venue-https` — алиасы единого запуска. Сертификат проверяется до Docker; Go/Vue и npm-зависимости при деплое не собираются. Отсутствующие runtime-образы и базовый Alpine скачиваются, имеющиеся не обновляются автоматически. HTTPS теперь используется smoke-проверкой по умолчанию. README и тесты приведены к единому поведению; `python3 tests/venue/deploy.py`, shell-синтаксис, Python-синтаксис, dry-run всех трёх Make-команд и `git diff --check` прошли; diff вручную проверен. Реальный перезапуск площадки с публичным сертификатом и сборка Docker-образа без WAN не выполнялись; Cudy и проверка телефонов остаются внешней контрольной точкой.
- Уточнение сборки по запросу пользователя: деплой больше не вызывает `make build`, Go/Vue-сборку или установку npm-зависимостей. Артефакты заранее готовятся существующим `make prod-build` в обеих папках; при отсутствии бинарника, Goose, bootstrap SQL, frontend или SQL-миграций запуск завершается до Docker с понятной ошибкой. `make venue-update` только упаковывает готовые файлы в Docker-образ и подготавливает runtime-образы. README обновлён; тесты первого, автономного и update-запуска подтверждают отсутствие make/npm, ошибки отсутствующих артефактов и сохранность настроек. `python3 tests/venue/deploy.py`, `python3 tests/venue/https.py`, `bash -n venue/deploy.sh`, `git diff --check` прошли; diff вручную проверен. Повторная реальная сборка приложения и запуск площадки не выполнялись; код дочерних репозиториев не менялся. - Уточнение сборки по запросу пользователя: деплой больше не вызывает `make build`, Go/Vue-сборку или установку npm-зависимостей. Артефакты заранее готовятся существующим `make prod-build` в обеих папках; при отсутствии бинарника, Goose, bootstrap SQL, frontend или SQL-миграций запуск завершается до Docker с понятной ошибкой. `make venue-update` только упаковывает готовые файлы в Docker-образ и подготавливает runtime-образы. README обновлён; тесты первого, автономного и update-запуска подтверждают отсутствие make/npm, ошибки отсутствующих артефактов и сохранность настроек. `python3 tests/venue/deploy.py`, `python3 tests/venue/https.py`, `bash -n venue/deploy.sh`, `git diff --check` прошли; diff вручную проверен. Повторная реальная сборка приложения и запуск площадки не выполнялись; код дочерних репозиториев не менялся.
- Подготовка этапа 3: добавлены `make venue-https`, отдельное Compose-дополнение и Nginx-конфигурация для прежних доменов интерфейса/API. HTTP перенаправляется на HTTPS с сохранением пути и параметров; API и материалы доступны через домен интерфейса и прежний API-домен. HTTPS-дополнение задаёт публичные адреса ссылок без изменения `.env`. Сертификат и ключ подключаются только для чтения из игнорируемого каталога; до Docker проверяются доверие полной цепочке, оба домена, срок действия и соответствие ключа. Повторный HTTPS-запуск перечитывает сертификат без пересборки. Обычный HTTP-запуск сохранён; добавлены HTTPS-smoke и инструкции DNS-01, LAN, продления и ручной приёмки. Код Go/Vue, protobuf, схема БД и конфигурация `prod` не менялись; новых зависимостей нет. - Подготовка этапа 3: добавлены `make venue-https`, отдельное Compose-дополнение и Nginx-конфигурация для прежних доменов интерфейса/API. HTTP перенаправляется на HTTPS с сохранением пути и параметров; API и материалы доступны через домен интерфейса и прежний API-домен. HTTPS-дополнение задаёт публичные адреса ссылок без изменения `.env`. Сертификат и ключ подключаются только для чтения из игнорируемого каталога; до Docker проверяются доверие полной цепочке, оба домена, срок действия и соответствие ключа. Повторный HTTPS-запуск перечитывает сертификат без пересборки. Обычный HTTP-запуск сохранён; добавлены HTTPS-smoke и инструкции DNS-01, LAN, продления и ручной приёмки. Код Go/Vue, protobuf, схема БД и конфигурация `prod` не менялись; новых зависимостей нет.
- Проверки подготовки этапа 3: `python3 tests/venue/deploy.py`, `python3 tests/venue/https.py --docker`, `bash -n venue/deploy.sh` и `git diff --check` прошли; Python-файлы проверены на синтаксис. Проверены реальный Compose merge, прежние тома и внутренняя сеть backend. Временные Docker-контейнеры с тестовым CA подтвердили проверяемый TLS/SNI, оба домена, redirects, API/материалы, SPA-пути с параметрами, перезапуск и замену сертификата через reload; свои ресурсы тест удалил. Проверки сертификатов покрывают отсутствующие/повреждённые файлы, неверный и зашифрованный ключ, недоверенную/неполную цепочку, отсутствие каждого домена и истечение срока; ошибка возникает до Docker, повторный готовый запуск не скачивает и не собирает образы, `.env` сохраняется. Ручное ревью diff выполнено; стандарты Go/Vue не затронуты. Реальное HTTPS-развёртывание, системное доверие телефонов, Cudy и WAN-off smoke не проверены из-за отсутствия оборудования/сертификата. Commit/push не выполнялись. - Проверки подготовки этапа 3: `python3 tests/venue/deploy.py`, `python3 tests/venue/https.py --docker`, `bash -n venue/deploy.sh` и `git diff --check` прошли; Python-файлы проверены на синтаксис. Проверены реальный Compose merge, прежние тома и внутренняя сеть backend. Временные Docker-контейнеры с тестовым CA подтвердили проверяемый TLS/SNI, оба домена, redirects, API/материалы, SPA-пути с параметрами, перезапуск и замену сертификата через reload; свои ресурсы тест удалил. Проверки сертификатов покрывают отсутствующие/повреждённые файлы, неверный и зашифрованный ключ, недоверенную/неполную цепочку, отсутствие каждого домена и истечение срока; ошибка возникает до Docker, повторный готовый запуск не скачивает и не собирает образы, `.env` сохраняется. Ручное ревью diff выполнено; стандарты Go/Vue не затронуты. Реальное HTTPS-развёртывание, системное доверие телефонов, Cudy и WAN-off smoke не проверены из-за отсутствия оборудования/сертификата. Commit/push не выполнялись.
- Следующий шаг: получить параметры Cudy/IP ноутбука и возможность DNS-проверки; выпустить отдельный доверенный сертификат, вручную настроить локальный DNS, запустить `make venue-https` и проверить этап 3 на Android/iPhone без WAN, затем последовательно выполнить этапы 4–15. Прежние адреса сами по себе не переносят production-игры и билеты. Общая задача не завершена и не переносится в архив. - Подтверждение пользователя: venue работает в браузере ноутбука и на телефоне после настройки DNS. Проверка Android/iPhone при отключённом WAN и мобильных данных ещё не подтверждена.
- Проверка ноутбука после изменения настроек роутера пользователем: системный DNS разрешает UI-домен в 192.168.10.155; HTTPS UI и API возвращают HTTP 200 с этого IP, сертификат проходит проверку. В DHCP также получен второй DNS 192.168.10.10; его назначение не проверено. Пользователь подтвердил открытие venue на телефоне, проверка без WAN ещё не выполнена. В браузере ноутбука остаётся проверить обновление DNS-кэша и использование системного DNS.
- Результат настройки DHCP: по запросу пользователя в Cudy сохранён Preferred DNS=192.168.10.155, Alternate DNS пустой. Повторное чтение формы подтвердило значения и сохранение остальных настроек DHCP; проверка локальных доменов по UDP/TCP прошла. Получение DNS клиентами после обновления DHCP-аренды ещё не проверено.
- Следующий шаг: разрешить LAN TCP/UDP 53 и TCP 80/443 на ноутбуке, переподключить устройства и проверить этап 3 на Android/iPhone без WAN, затем последовательно выполнить этапы 4–15. IP/MAC Binding ноутбука сохранён, DNS и HTTPS уже запущены. Прежние адреса сами по себе не переносят production-игры и билеты. Общая задача не завершена и не переносится в архив.
### ED-024 — Общая дизайн-система фронтенда ### ED-024 — Общая дизайн-система фронтенда
+5
View File
@@ -13,6 +13,9 @@
сети, проверяет TLS, маршруты, перенаправления и перезапуск, затем удаляет сети, проверяет TLS, маршруты, перенаправления и перезапуск, затем удаляет
свои контейнеры и сеть. Использует локальный `nginx:alpine` и временный тестовый CA; свои контейнеры и сеть. Использует локальный `nginx:alpine` и временный тестовый CA;
работающие окружения, production и системное хранилище доверия не меняет. работающие окружения, production и системное хранилище доверия не меняет.
- `dns.py --docker` — проверяет оба локальных домена по UDP/TCP и отсутствие
внешних IPv6-ответов в отдельной внутренней Docker-сети без WAN до и после
перезапуска CoreDNS. `--server <IP ноутбука>` проверяет работающий DNS.
Команды из корня проекта: Команды из корня проекта:
@@ -21,4 +24,6 @@ python3 tests/venue/deploy.py
python3 tests/venue/smoke.py --verify python3 tests/venue/smoke.py --verify
python3 tests/venue/https.py python3 tests/venue/https.py
python3 tests/venue/https.py --docker python3 tests/venue/https.py --docker
python3 tests/venue/dns.py --docker
python3 tests/venue/dns.py --server 192.168.10.155
``` ```
+122
View File
@@ -0,0 +1,122 @@
"""Verify venue DNS over UDP/TCP; --docker tests an isolated server without WAN."""
import argparse
from pathlib import Path
import os
import socket
import struct
import subprocess
import time
import uuid
DOMAINS = ['evening-detective.crabs-games.art', 'evening-detective-api.crabs-games.art']
IMAGE = 'coredns/coredns:1.14.7'
def query(server, domain, kind=1, tcp=False):
identifier = os.urandom(2)
question = b''.join(bytes([len(label)]) + label.encode() for label in domain.split('.')) + b'\0'
packet = identifier + struct.pack('!5H', 0x0100, 1, 0, 0, 0) + question + struct.pack('!2H', kind, 1)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM if tcp else socket.SOCK_DGRAM) as connection:
connection.settimeout(3)
connection.connect((server, 53))
if tcp:
connection.sendall(struct.pack('!H', len(packet)) + packet)
def receive(size):
data = b''
while len(data) < size:
chunk = connection.recv(size - len(data))
if not chunk:
raise RuntimeError('Incomplete DNS response')
data += chunk
return data
response = receive(struct.unpack('!H', receive(2))[0])
else:
connection.send(packet)
response = connection.recv(4096)
assert response[:2] == identifier, 'DNS response identifier mismatch'
flags, questions, answers, _, _ = struct.unpack_from('!5H', response, 2)
assert flags & 0x8000 and not flags & 0x0200, 'Invalid or truncated DNS response'
def skip_name(offset):
while response[offset]:
if response[offset] & 0xc0 == 0xc0:
return offset + 2
offset += response[offset] + 1
return offset + 1
offset = 12
for _ in range(questions):
offset = skip_name(offset) + 4
addresses = []
for _ in range(answers):
offset = skip_name(offset)
record_type, _, _, length = struct.unpack_from('!HHIH', response, offset)
offset += 10
if record_type in (1, 28):
addresses.append(socket.inet_ntop(socket.AF_INET if record_type == 1 else socket.AF_INET6,
response[offset:offset + length]))
offset += length
return flags & 15, addresses
def verify(server, expected):
for domain in DOMAINS:
for tcp in (False, True):
assert query(server, domain, tcp=tcp) == (0, [expected]), domain
assert query(server, domain, kind=28, tcp=tcp) == (0, []), 'Unexpected external IPv6 answer: ' + domain
print('PASS: both venue domains resolve locally over UDP/TCP, no external IPv6 answers')
def docker_test():
root = Path(__file__).resolve().parents[2]
network = 'venue-dns-test-' + uuid.uuid4().hex[:10]
container = network + '-server'
def run(*args):
return subprocess.check_output(args, text=True, stderr=subprocess.PIPE).strip()
try:
run('docker', 'network', 'create', '--internal', network)
run('docker', 'run', '-d', '--name', container, '--network', network,
'-e', 'VENUE_DNS_IP=192.0.2.55', '-v', str(root / 'venue/Corefile') + ':/etc/coredns/Corefile:ro',
IMAGE, '-conf', '/etc/coredns/Corefile')
address = run('docker', 'inspect', '--format', '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}', container)
for attempt in range(30):
try:
query(address, DOMAINS[0])
break
except (OSError, RuntimeError):
time.sleep(0.1)
else:
raise AssertionError('DNS did not start')
verify(address, '192.0.2.55')
assert run('docker', 'network', 'inspect', '--format', '{{.Internal}}', network) == 'true'
run('docker', 'restart', container)
for attempt in range(30):
try:
query(address, DOMAINS[0])
break
except (OSError, RuntimeError):
time.sleep(0.1)
verify(address, '192.0.2.55')
print('PASS: CoreDNS works without WAN before and after restart')
finally:
subprocess.run(['docker', 'rm', '-f', container], capture_output=True)
subprocess.run(['docker', 'network', 'rm', network], capture_output=True)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--docker', action='store_true')
parser.add_argument('--server', help='Laptop IPv4 address')
args = parser.parse_args()
if args.docker:
docker_test()
elif args.server:
verify(args.server, args.server)
else:
parser.error('Use --docker or --server <laptop IPv4>')
+8
View File
@@ -103,6 +103,7 @@ with tempfile.TemporaryDirectory(prefix="venue-https-") as directory:
assert prefix + " build --pull=false" in commands assert prefix + " build --pull=false" in commands
assert commands.index(prefix + " build --pull=false") < commands.index(prefix + " up -d") assert commands.index(prefix + " build --pull=false") < commands.index(prefix + " up -d")
assert (prefix + " pull " in commands) == cold assert (prefix + " pull " in commands) == cold
assert (prefix + " pull dns" in commands) == cold
assert ("docker pull alpine:3.22" in commands) == cold assert ("docker pull alpine:3.22" in commands) == cold
assert "make " not in commands and "npm " not in commands assert "make " not in commands and "npm " not in commands
else: else:
@@ -173,6 +174,13 @@ with tempfile.TemporaryDirectory(prefix="venue-https-") as directory:
assert len(configs) == 1 and configs[0]["source"].endswith("/nginx.https.conf") and configs[0]["read_only"] assert len(configs) == 1 and configs[0]["source"].endswith("/nginx.https.conf") and configs[0]["read_only"]
assert any(v["target"] == "/etc/nginx/certificates" and v["read_only"] for v in https["services"]["proxy"]["volumes"]) assert any(v["target"] == "/etc/nginx/certificates" and v["read_only"] for v in https["services"]["proxy"]["volumes"])
assert https["networks"]["default"]["internal"] and https["volumes"] == http_config["volumes"] assert https["networks"]["default"]["internal"] and https["volumes"] == http_config["volumes"]
dns = https["services"]["dns"]
assert dns["image"] == "coredns/coredns:1.14.7"
assert dns["environment"]["VENUE_DNS_IP"] == "127.0.0.1"
assert set(dns["networks"]) == {"entrance"}
assert {(p["protocol"], str(p["published"])) for p in dns["ports"]} == {("udp", "53"), ("tcp", "53")}
assert all(p["host_ip"] == "127.0.0.1" for p in dns["ports"])
assert any(v["target"] == "/etc/coredns/Corefile" and v["read_only"] for v in dns["volumes"])
print("PASS: Compose merge, HTTPS addresses, ports, read-only mounts and preserved data volumes") print("PASS: Compose merge, HTTPS addresses, ports, read-only mounts and preserved data volumes")
suffix = uuid.uuid4().hex[:10] suffix = uuid.uuid4().hex[:10]
network = "venue-https-test-" + suffix network = "venue-https-test-" + suffix
+14
View File
@@ -0,0 +1,14 @@
evening-detective.crabs-games.art evening-detective-api.crabs-games.art {
hosts {
{$VENUE_DNS_IP} evening-detective.crabs-games.art evening-detective-api.crabs-games.art
ttl 60
no_reverse
}
errors
}
. {
forward . 1.1.1.1 8.8.8.8
cache 30
errors
}
+1 -1
View File
@@ -68,7 +68,7 @@ if [[ ! -d migrations ]] || ! compgen -G 'migrations/*.sql' >/dev/null; then
exit 1 exit 1
fi fi
for service_image in postgres_evening_detective_server=postgres:15 rustfs_evening_detective_server=rustfs/rustfs:latest evening_detective_frontend=byjg/static-httpserver:latest proxy=nginx:alpine; do for service_image in postgres_evening_detective_server=postgres:15 rustfs_evening_detective_server=rustfs/rustfs:latest evening_detective_frontend=byjg/static-httpserver:latest proxy=nginx:alpine dns=coredns/coredns:1.14.7; do
if ! docker image inspect "${service_image#*=}" >/dev/null 2>&1; then if ! docker image inspect "${service_image#*=}" >/dev/null 2>&1; then
"${compose[@]}" pull "${service_image%%=*}" "${compose[@]}" pull "${service_image%%=*}"
fi fi
+14
View File
@@ -1,4 +1,18 @@
services: services:
dns:
image: coredns/coredns:1.14.7
command: [-conf, /etc/coredns/Corefile]
environment:
VENUE_DNS_IP: ${VENUE_BIND_IP:-127.0.0.1}
volumes:
- ./Corefile:/etc/coredns/Corefile:ro
ports:
- '${VENUE_BIND_IP:-127.0.0.1}:53:53/udp'
- '${VENUE_BIND_IP:-127.0.0.1}:53:53/tcp'
networks:
- entrance
restart: unless-stopped
evening_detective_server: evening_detective_server:
environment: environment:
APP_BASE_URL: https://evening-detective.crabs-games.art APP_BASE_URL: https://evening-detective.crabs-games.art