# TestY on famedev — установка и эксплуатация

Дата актуализации: 2026-09-07

## Что это

На сервере famedev поднят отдельный экземпляр **TestY TMS** для проекта Кирилла.

Публичный URL:

- <https://testy.kirill.cv.famedev.ru/>

Исходный upstream TestY:

- <https://gitlab-pub.yadro.com/testy/testy>
- Исторически при подготовке также смотрели mirror/compose: `https://github.com/YADRO-KNS/testy-tms`

Историческая сессия Hermes, где обсуждали и начинали установку: `@session:default/20260810_133808_bd508e91`.

## Текущая схема

Важно: текущий TestY **не является полноценным Coolify Application**. Он поднят как ручной Docker Compose stack, но использует общий Coolify/Traefik proxy для публичного домена.

Схема:

```text
https://testy.kirill.cv.famedev.ru
        ↓ DNS на VPS
Coolify Traefik proxy
        ↓ docker network: coolify
container: testy-nginx
        ↓ docker network: testy
testy Django app + postgres + redis + celery + notifications
```

Код/рабочая директория:

```bash
/srv/projects/Testy/testy
```

Основной compose для этого сервера:

```bash
/srv/projects/Testy/testy/docker-compose.deploy.yml
```

Nginx template под Traefik:

```bash
/srv/projects/Testy/testy/nginx/testy-traefik.conf.template
```

Секреты/переменные лежат в `.env` рядом с compose. Не печатать и не коммитить:

```bash
/srv/projects/Testy/testy/.env
```

## Сервисы/контейнеры

Ожидаемые контейнеры:

```text
testy-nginx              nginx, публичный вход через Traefik, сети: coolify,testy
testy                    Django/Gunicorn app, порт 8000 внутри сети testy
testy_celery             Celery worker/beat
testy-notifications      Django notifications worker
testy_pgbouncer          PgBouncer
testy_db                 PostgreSQL 14.5
testy_redis              Redis
testy-frontend           build-only контейнер, нормально если Exited(0)
```

Проверка:

```bash
cd /srv/projects/Testy/testy
docker compose -f docker-compose.deploy.yml -p testy ps

docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Networks}}' | grep -E '^testy|testy-'
```

`testy-frontend` может быть `Exited (0)` — это нормально: он собирает статический frontend в volume/build, который потом отдаёт `testy-nginx`.

## Публичная проверка

```bash
curl --noproxy '*' -sS -I --max-time 20 https://testy.kirill.cv.famedev.ru/

curl --noproxy '*' -sS --max-time 20 \
  https://testy.kirill.cv.famedev.ru/.well-known/project.json
```

Ожидаемо:

```text
HTTP/2 200
server: nginx/1.25.1
```

`/.well-known/project.json` должен вернуть JSON примерно такого вида:

```json
{
  "name": "TestY TMS",
  "status": "running",
  "stack": ["Django", "React", "PostgreSQL", "Redis", "Celery", "Nginx"]
}
```

Swagger/API:

```text
https://testy.kirill.cv.famedev.ru/api/v2/swagger/
https://testy.kirill.cv.famedev.ru/api/
```

## Как был адаптирован compose

Официальный compose TestY публикует host-порты и рассчитан на прямой nginx/SSL. Для famedev его адаптировали так:

1. Убрали публичные host ports у backend/db/redis.
2. Оставили публичный вход только через `testy-nginx`.
3. `testy-nginx` подключили к двум сетям:
   - `testy` — внутренняя сеть TestY stack
   - `coolify` — внешняя сеть Coolify Traefik proxy
4. На `testy-nginx` добавили Traefik labels:

```yaml
labels:
  - traefik.enable=true
  - traefik.docker.network=coolify
  - traefik.http.middlewares.testy-https-redirect.redirectscheme.scheme=https
  - traefik.http.middlewares.testy-https-redirect.redirectscheme.permanent=true
  - traefik.http.routers.testy-http.rule=Host(`testy.kirill.cv.famedev.ru`)
  - traefik.http.routers.testy-http.entrypoints=http
  - traefik.http.routers.testy-http.middlewares=testy-https-redirect
  - traefik.http.routers.testy-https.rule=Host(`testy.kirill.cv.famedev.ru`)
  - traefik.http.routers.testy-https.entrypoints=https
  - traefik.http.routers.testy-https.tls=true
  - traefik.http.routers.testy-https.tls.certresolver=letsencrypt
  - traefik.http.services.testy.loadbalancer.server.port=80
```

5. В nginx template настроили проксирование API/WebSocket на backend:

```nginx
location ~ ^/(admin|api|api-auth|auth|swagger|plugins|celery-progress|attachments|avatars)/ {
    proxy_pass http://testy:8000;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_read_timeout 300s;
}

location ~ ^/(ws)/ {
    proxy_pass http://testy:8000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto https;
}
```

## Env-переменные

Не публиковать значения. Для ориентира нужны такие ключи:

```env
POSTGRES_NAME=
POSTGRES_USER=
POSTGRES_USERNAME=
POSTGRES_PASSWORD=
POSTGRES_SERVICE_HOST=
POSTGRES_SERVICE_PORT=
DB_HOST=
COMPANY_DOMAIN=testy.kirill.cv.famedev.ru
SUPERUSER_USERNAME=
SUPERUSER_PASSWORD=
SECRET_KEY=
ALLOWED_HOSTS=
CELERY_BROKER_URL=
CELERY_RESULT_BACKEND=
REDIS_HOST=
REDIS_PORT=
VITE_APP_API_ROOT=https://testy.kirill.cv.famedev.ru
HOST_NAME=testy.kirill.cv.famedev.ru
VOLUMES_PATH=./volumes
UID=
GID=
```

Для Docker-сети правильнее использовать внутренние имена:

```env
POSTGRES_SERVICE_HOST=db
POSTGRES_SERVICE_PORT=5432
DB_HOST=db
REDIS_HOST=redis
REDIS_PORT=6379
CELERY_BROKER_URL=redis://redis:6379
CELERY_RESULT_BACKEND=redis://redis:6379
```

## Запуск / рестарт

```bash
cd /srv/projects/Testy/testy

docker compose -f docker-compose.deploy.yml -p testy up -d --build
```

Посмотреть логи:

```bash
cd /srv/projects/TestY/testy  # если путь с большой Y в системе: /srv/projects/Testy/testy

docker compose -f docker-compose.deploy.yml -p testy logs --tail=120 testy
docker compose -f docker-compose.deploy.yml -p testy logs --tail=120 nginx
docker compose -f docker-compose.deploy.yml -p testy logs --tail=120 celery
```

Аккуратный рестарт без пересборки:

```bash
cd /srv/projects/Testy/testy
docker compose -f docker-compose.deploy.yml -p testy up -d
```

Пересборка frontend/backend:

```bash
cd /srv/projects/Testy/testy
docker compose -f docker-compose.deploy.yml -p testy up -d --build client testy celery notification-worker nginx
```

## Backup перед апдейтом

Перед обновлением TestY обязательно сохранить БД и attachments/media.

Пример БД backup:

```bash
mkdir -p /srv/projects/Testy/backups
cd /srv/projects/Testy/testy

docker exec testy_db pg_dump -U postgres postgres \
  > /srv/projects/Testy/backups/testy_$(date -u +%Y%m%dT%H%M%SZ).sql
```

Если пользователь/БД в `.env` не `postgres`, брать значения из `.env`, не печатая секреты.

Media/volumes:

```bash
cd /srv/projects/Testy

tar -czf backups/testy_media_$(date -u +%Y%m%dT%H%M%SZ).tar.gz \
  testy/backend/testy/media testy/volumes/media testy/volumes/build
```

## Подключение автотестов/portfolio к TestY

В portfolio repo есть скрипты, которые умеют seed/report в TestY:

```bash
/srv/projects/portfolio/scripts/seed_testy.py
/srv/projects/portfolio/scripts/seed_poltava_testy.py
/srv/projects/portfolio/scripts/report_to_testy.py
/srv/projects/portfolio/scripts/report_poltava_to_testy.py
/srv/projects/portfolio/scripts/run_poltava_smoke.sh
```

Базовая схема:

```bash
cd /srv/projects/portfolio

TESTY_URL=https://testy.kirill.cv.famedev.ru \
TESTY_USER=<admin user> \
TESTY_PASSWORD=<secret> \
python scripts/seed_testy.py

TESTY_URL=https://testy.kirill.cv.famedev.ru \
TESTY_USER=<admin user> \
TESTY_PASSWORD=<secret> \
python scripts/seed_poltava_testy.py

BASE_URL=https://fameresort.ru/ \
FAME_LOGIN=<secret> \
FAME_PASSWORD=<secret> \
TESTY_URL=https://testy.kirill.cv.famedev.ru \
TESTY_USER=<admin user> \
TESTY_PASSWORD=<secret> \
PUSH_TESTY=1 \
scripts/run_poltava_smoke.sh
```

## Важные нюансы

- Это ручной compose stack, а не штатное Coolify приложение. Coolify здесь используется только как Traefik/proxy network.
- Не публиковать наружу Postgres/Redis/PgBouncer порты без необходимости.
- `testy-frontend` в production может завершаться после сборки — это expected behavior.
- `testy-nginx` должен быть подключён к сети `coolify`, иначе Traefik не увидит роут.
- При правках домена менять labels в `docker-compose.deploy.yml` и `server_name`/`HOST_NAME`/`VITE_APP_API_ROOT`.
- Перед апгрейдом TestY всегда делать backup БД и media.
