Добавлены уведомления
This commit is contained in:
@@ -48,6 +48,12 @@ rsync -rltD --no-perms --no-owner --no-group -ivc --delete \
|
||||
--exclude='*.pem' \
|
||||
--exclude='*.key' \
|
||||
--exclude='secrets/' \
|
||||
--exclude='node_modules/' \
|
||||
--exclude='dist/' \
|
||||
--exclude='.expo/' \
|
||||
--exclude='playwright-report/' \
|
||||
--exclude='test-results/' \
|
||||
--exclude='*.log' \
|
||||
-e "ssh -i ~/.ssh/hansel" \
|
||||
/mnt/c/Users/MI/Documents/Assistent/HAN_chat_specification/codebase/backend/ \
|
||||
root@135.106.164.58:/opt/han-chat/backend/
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user