Реализация на отдельных двух машинах с протестированным взаимодействием по проверке сообщений

This commit is contained in:
mi
2026-08-19 18:24:00 +03:00
parent bbef7a30c9
commit c7a80e7256
103 changed files with 3457 additions and 3725 deletions
@@ -0,0 +1,317 @@
## Накатывание Notification Center v1
Выполнять на сервере из каталога:
```sh
cd /path/to/HAN_chat_specification/codebase/backend
```
### 1. Подготовить резервную точку
Создайте PITR-маркер/снимок PostgreSQL. Миграция forward-only — откатывать её нельзя.
### 2. Обновить код
```sh
git fetch
git checkout <утверждённый-commit-or-tag>
git status --short
```
Рабочее дерево должно быть чистым.
### 3. Добавить переменные в `.env`
Не перезаписывайте существующий `.env`. Добавьте:
```sh
NOTIFICATIONS_TOKEN_PRODUCER_TEST=<случайный-токен>
NGINX_RATE_LIMIT_NOTIFICATIONS_READ=120r/m
NGINX_RATE_LIMIT_NOTIFICATIONS_ACTION=60r/m
NGINX_RATE_LIMIT_NOTIFICATION_UPLOAD=20r/m
NGINX_RATE_LIMIT_NOTIFICATIONS_PUBLIC=60r/m
```
Токен можно создать так:
```sh
openssl rand -hex 32
chmod 600 .env
```
Проверить конфигурацию:
```sh
./scripts/validate-env .env
docker compose --env-file .env config --quiet
```
### 4. Собрать новые образы
```sh
docker compose --env-file .env build --pull \
api-backend frontend-static nginx
```
### 5. Выполнить миграцию
```sh
PITR_MARKER_CONFIRMED=true ENV_FILE=.env \
deployment/scripts/migrate.sh
deployment/scripts/seed.sh
```
Ожидаемая ревизия:
```text
0008_notifications_v1
```
Проверка:
```sh
docker compose --env-file .env --profile ops run --rm \
migrate-api alembic current
```
### 6. Обновить статический frontend
```sh
docker compose --env-file .env run --rm frontend-static
```
### 7. Перезапустить изменённые сервисы
```sh
docker compose --env-file .env up -d --force-recreate \
api-backend \
delivery-worker \
safety-recovery-worker \
cleanup-worker \
notification-expire-worker \
notification-draft-cleanup-worker \
nginx
```
### 8. Проверить состояние
```sh
docker compose ps
docker compose logs --since=10m \
api-backend \
notification-expire-worker \
notification-draft-cleanup-worker \
nginx
docker compose exec -T nginx nginx -t -c /tmp/nginx.conf
```
Проверить API:
```sh
curl -fsS https://chat.han0107.ru/health/live
curl -fsS https://chat.han0107.ru/health/ready
curl -fsS https://chat.han0107.ru/api/v1/public/notification-types
curl -fsS https://chat.han0107.ru/api/v1/public/notifications
```
Внешний internal endpoint обязан возвращать `404`:
```sh
curl -i https://chat.han0107.ru/internal/notifications/v1/notifications
```
### 9. Провести smoke-тест
```sh
deployment/scripts/smoke.sh
```
Дополнительно проверить через `producer_test`:
- Create возвращает `201`;
- повтор того же тела — `200`;
- то же `external_id` с изменённым телом — `409`;
- Cancel — `200`;
- повторный Cancel — `200`;
- уведомление появляется у указанного `user_id`.
Полный эксплуатационный чек-лист находится в `codebase/backend/deployment/RUNBOOK.ru.md`.
Важно: при проблеме не выполнять `docker compose down -v` и не откатывать Alembic. После применения `0008` безопасный путь — исправляющий релиз; старый backend может не пройти readiness из-за проверки версии схемы.
# Создание уведомлений
## Публичные (инсерт в БД)
BEGIN;
INSERT INTO han_app.guest_notifications (
id,
notification_type,
notification_datetime,
header,
text,
priority_override,
date_expired,
price,
old_price,
instruction_url,
chat_message_text,
lifecycle_status,
closed_at,
record_status,
status_changed_at,
status_change_reason,
created_at,
updated_at,
updater_user_id
)
VALUES
-- 1. Авторизация
(
'019fa3e9-8d91-7199-8199-609916d48f04',
'authorize',
now(),
'Войдите в личный кабинет',
'Авторизуйтесь, чтобы видеть персональные статусы, документы и уведомления.',
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
'active',
NULL,
'A',
NULL,
NULL,
now(),
now(),
NULL
),
-- 2. Установка приложения
(
'019fa3e9-8d91-722a-90df-e4824e23b354',
'install_app',
now(),
'Установите приложение HAN',
'Добавьте приложение на главный экран, чтобы сервис всегда был под рукой.',
NULL,
NULL,
NULL,
NULL,
'https://www.han0107.ru/app/how-install-pwa',
NULL,
'active',
NULL,
'A',
NULL,
NULL,
now(),
now(),
NULL
),
-- 3. Глобальная акция
(
'019fa3e9-8d91-7d19-8873-eefb2a60f116',
'promo_global',
now(),
'Специальное предложение',
'Узнайте подробнее об актуальной акции.',
NULL,
now() + interval '30 days',
NULL,
NULL,
NULL,
'Здравствуйте! Хочу узнать подробнее об акции.',
'active',
NULL,
'A',
NULL,
NULL,
now(),
now(),
NULL
),
-- 4. Глобальное предложение
(
'019fa3e9-8d91-7452-a13d-3d2518ea257d',
'ads_global',
now(),
'Нужна помощь?',
'Расскажем об услугах и подберём подходящее решение.',
NULL,
now() + interval '30 days',
NULL,
NULL,
NULL,
'Здравствуйте! Хочу получить консультацию по услугам.',
'active',
NULL,
'A',
NULL,
NULL,
now(),
now(),
NULL
)
ON CONFLICT (id) DO UPDATE SET
notification_datetime = EXCLUDED.notification_datetime,
header = EXCLUDED.header,
text = EXCLUDED.text,
priority_override = EXCLUDED.priority_override,
date_expired = EXCLUDED.date_expired,
price = EXCLUDED.price,
old_price = EXCLUDED.old_price,
instruction_url = EXCLUDED.instruction_url,
chat_message_text = EXCLUDED.chat_message_text,
lifecycle_status = 'active',
closed_at = NULL,
record_status = 'A',
status_changed_at = now(),
status_change_reason = 'guest_campaign_republished',
updated_at = now();
COMMIT;
## Персональные
Запускать из /opt/han-chat/backend. Публичный nginx не пропускает internal API, поэтому используем контейнер в backend-сети.
read -rsp "NOTIFICATIONS_TOKEN_PRODUCER_TEST: " TOKEN
echo
read -rp "USER_ID клиента: " USER_ID
NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
EXTERNAL_ID="manual-news-$(date +%s)"
docker run --rm -i \
--network han-chat-backend \
curlimages/curl:latest \
-sS -i \
-X POST \
'http://api-backend:8000/internal/notifications/v1/notifications' \
-H "Authorization: Bearer ${TOKEN}" \
-H 'Content-Type: application/json' \
--data-binary @- <<JSON
{
"user_id": "${USER_ID}",
"notification_type": "news",
"source": "producer_test",
"external_id": "${EXTERNAL_ID}",
"notification_datetime": "${NOW}",
"header": "Тестовое уведомление",
"text": "Уведомление создано через Internal API",
"details": {
"details_header": "Проверка Notification Center",
"details_text": "Это тестовое уведомление от producer_test."
}
}
JSON