Косметические правки по фронту, раскатка приложения в продакшн версии, исправление бага с созданием контакта

This commit is contained in:
mi
2026-09-03 11:34:25 +03:00
parent f989097484
commit 465a70d488
18 changed files with 528 additions and 139 deletions
@@ -1,58 +1,78 @@
#!/usr/bin/env bash
# Создаёт 9 персональных уведомлений всех видов контура P для одного user_id.
# Запускать на ВМ из каталога backend: /opt/han-chat/backend
# Запускать на ВМ1 под root/admin (sudo -i). Секреты не читаются из /etc/han/vm1.env.
#
# cd /opt/han-chat/backend
# sed -i 's/\r$//' deployment/scripts/seed-personal-notifications-test.sh
# chmod +x deployment/scripts/seed-personal-notifications-test.sh
# ./deployment/scripts/seed-personal-notifications-test.sh
# sed -i 's/\r$//' /opt/han-chat/current/backend/deployment/scripts/seed-personal-notifications-test.sh
# chmod +x /opt/han-chat/current/backend/deployment/scripts/seed-personal-notifications-test.sh
# /opt/han-chat/current/backend/deployment/scripts/seed-personal-notifications-test.sh
#
# Токен берётся из .env (NOTIFICATIONS_TOKEN_PRODUCER_TEST) — тот же, что у api-backend.
# При старте api-backend синхронизирует hash токена в notification_sources.
# Токен: runtime-файл NOTIFICATIONS_TOKEN_PRODUCER_TEST
# (/run/han-chat/secrets/…, тот же, что у api-backend). Hash в notification_sources
# синхронизируется при старте api-backend.
set -euo pipefail
cd "$(dirname "$0")/../.."
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
if [[ -f "${SCRIPT_DIR}/../../docker-compose.yml" ]]; then
cd "${SCRIPT_DIR}/../.."
else
cd "${HAN_DEPLOY_DIR:-/opt/han-chat/current/backend}"
fi
ENV_FILE="${ENV_FILE:-.env}"
CONFIG_FILE="${CONFIG_FILE:-/etc/han/vm1.env}"
SECRETS_LAUNCHER="${SECRETS_LAUNCHER:-/usr/local/lib/han-secrets/han-secrets}"
if [[ ! -x "$SECRETS_LAUNCHER" ]]; then
SECRETS_LAUNCHER="deployment/secrets/han-secrets"
fi
if [[ ! -f "$ENV_FILE" ]]; then
echo "Не найден $ENV_FILE. Запускайте из каталога backend." >&2
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "Не найден конфиг ${CONFIG_FILE} (ожидается /etc/han/vm1.env)." >&2
exit 1
fi
env_value() {
python3 - "$ENV_FILE" "$1" <<'PY'
import sys
from pathlib import Path
if [[ "${HAN_SECRETS_ACTIVE:-0}" != "1" ]]; then
[[ -x "$SECRETS_LAUNCHER" ]] || {
echo "Secret launcher is required: $SECRETS_LAUNCHER" >&2
exit 66
}
exec "$SECRETS_LAUNCHER" run --config "$CONFIG_FILE" -- "$0" "$@"
fi
path, wanted = sys.argv[1:]
for raw in Path(path).read_text(encoding="utf-8").splitlines():
line = raw.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
if key.strip() == wanted:
value = value.strip()
if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'":
value = value[1:-1]
print(value)
break
else:
raise SystemExit(f"missing environment variable: {wanted}")
PY
compose() {
if [[ -x /usr/local/sbin/han-vm1-compose ]]; then
/usr/local/sbin/han-vm1-compose "$@"
else
docker compose --env-file "$CONFIG_FILE" "$@"
fi
}
trim_token() {
printf '%s' "$1" | tr -d '\r\n\t '
}
read_secret_file() {
local path="${1:-}"
[[ -n "$path" && -r "$path" ]] || return 1
python3 - "$path" <<'PY'
from pathlib import Path
import sys
print(Path(sys.argv[1]).read_text(encoding="utf-8").strip())
PY
}
TOKEN="$(trim_token "${NOTIFICATIONS_TOKEN_PRODUCER_TEST:-}")"
if [[ -z "$TOKEN" ]]; then
TOKEN="$(trim_token "$(env_value NOTIFICATIONS_TOKEN_PRODUCER_TEST 2>/dev/null || true)")"
TOKEN="$(trim_token "$(read_secret_file "${NOTIFICATIONS_TOKEN_PRODUCER_TEST_FILE:-}" 2>/dev/null || true)")"
fi
if [[ -z "$TOKEN" ]]; then
read -rsp "NOTIFICATIONS_TOKEN_PRODUCER_TEST (из .env не найден): " TOKEN
TOKEN="$(trim_token "$(read_secret_file "${HAN_RUNTIME_SECRET_DIR:-/run/han-chat/secrets}/NOTIFICATIONS_TOKEN_PRODUCER_TEST" 2>/dev/null || true)")"
fi
if [[ -z "$TOKEN" ]]; then
TOKEN="$(trim_token "$(compose exec -T api-backend python3 -c 'import os; print(os.getenv("NOTIFICATIONS_TOKEN_PRODUCER_TEST") or "")' 2>/dev/null || true)")"
fi
if [[ -z "$TOKEN" ]]; then
read -rsp "NOTIFICATIONS_TOKEN_PRODUCER_TEST (runtime secret не найден): " TOKEN
echo
TOKEN="$(trim_token "$TOKEN")"
fi
@@ -65,7 +85,7 @@ if [[ -z "${TOKEN}" || -z "${USER_ID}" ]]; then
exit 1
fi
echo "Токен: ${#TOKEN} символов (первые 8: ${TOKEN:0:8}…)"
echo "Токен: ${#TOKEN} символов (значение не печатается)."
PAYLOAD_DIR="$(mktemp -d)"
trap 'rm -rf "$PAYLOAD_DIR"' EXIT
@@ -103,22 +123,48 @@ create_notification() {
fi
echo
echo "========== ${label} =========="
docker run --rm \
--network han-chat-backend \
-v "${payload_file}:/payload.json:ro" \
curlimages/curl:latest \
-sS -i \
-X POST \
'http://api-backend:8000/internal/notifications/v1/notifications' \
-H "Authorization: Bearer ${TOKEN}" \
-H 'Content-Type: application/json; charset=utf-8' \
--data-binary @/payload.json
compose exec -T \
-e "NOTIFICATIONS_PRODUCER_TOKEN=${TOKEN}" \
api-backend python3 -c "
import os
import sys
import urllib.error
import urllib.request
payload = sys.stdin.buffer.read()
request = urllib.request.Request(
'http://127.0.0.1:8000/internal/notifications/v1/notifications',
data=payload,
method='POST',
headers={
'Authorization': 'Bearer ' + os.environ['NOTIFICATIONS_PRODUCER_TOKEN'],
'Content-Type': 'application/json; charset=utf-8',
},
)
try:
with urllib.request.urlopen(request, timeout=60) as response:
sys.stdout.write('HTTP/1.1 %s\n' % response.status)
for key, value in response.headers.items():
sys.stdout.write('%s: %s\n' % (key, value))
sys.stdout.write('\n')
sys.stdout.write(response.read().decode('utf-8', errors='replace'))
sys.stdout.write('\n')
except urllib.error.HTTPError as exc:
sys.stdout.write('HTTP/1.1 %s\n' % exc.code)
for key, value in exc.headers.items():
sys.stdout.write('%s: %s\n' % (key, value))
sys.stdout.write('\n')
sys.stdout.write(exc.read().decode('utf-8', errors='replace'))
sys.stdout.write('\n')
if exc.code >= 500 or exc.code in (401, 403):
raise SystemExit(1)
" <"$payload_file"
}
prepare_docs_in_s3() {
echo >&2
echo "========== PREP: загрузка тестовых документов в S3 для docs_ready ==========" >&2
docker compose --env-file "$ENV_FILE" exec -T \
compose exec -T \
-e "USER_ID=${USER_ID}" \
-e "RUN_ID=${RUN_ID}" \
api-backend python3 - <<'PY'
@@ -443,6 +489,7 @@ echo
echo "========== Готово =========="
echo "External ID prefix: ${RUN_ID}-*"
echo
echo "Если видите 401: проверьте NOTIFICATIONS_TOKEN_PRODUCER_TEST в .env и перезапустите api-backend."
echo " grep NOTIFICATIONS_TOKEN_PRODUCER_TEST .env"
echo " docker compose --env-file .env up -d --force-recreate api-backend"
echo "Если видите 401: проверьте runtime-секрет NOTIFICATIONS_TOKEN_PRODUCER_TEST"
echo "и hash в han_app.notification_sources, затем пересоздайте api-backend:"
echo " test -r /run/han-chat/secrets/NOTIFICATIONS_TOKEN_PRODUCER_TEST"
echo " /usr/local/sbin/han-vm1-compose up -d --force-recreate api-backend"