- CRM_USE_STUB: local runs no longer reach the live CRM. With only the Checkbox stub, a stub receipt would still move the real order to PACKED. Refused in production, same as CHECKBOX_USE_STUB. - scripts/deploy.sh: backup, fast-forward main, build, health check and code rollback on failure. scripts/backup.sh: pg_dump with verification and 14-day rotation (used by cron and deploy.sh). - Gitea Actions CI: ruff + pytest, oxlint + build. - DEPLOY.md runbook; CLAUDE.md rules for safe local development. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8.2 KiB
Выкладка в прод
Где что
| Что | Где |
|---|---|
| Сервер | websrv, 192.168.88.100, Ubuntu; SSH: пользователь deploy, порт 22 (алиас lux-prod в ~/.ssh/config разработчика) |
| Приложение | https://asist.ystyle.com.ua |
| Код | /home/deploy/lux_fiscal — клон main из Gitea (deploy key только на чтение, ~/.ssh/config → Host gitea = 127.0.0.1:2222) |
| Секреты | /home/deploy/lux_fiscal/.env (права 600, в git не попадает) |
| Бэкапы | /home/deploy/backups/lux_fiscal/*.dump, журнал — backup.log там же |
| TLS / домен | Nginx Proxy Manager на том же сервере (http://192.168.88.100:81), Proxy Host asist.ystyle.com.ua → lux-fiscal-frontend:80 |
Сервисы поднимаются из двух файлов: docker-compose.yml + docker-compose.prod.yml (прод-оверлей убирает порты на хосте
и подключает frontend к внешней сети web, где живёт NPM). Во всех командах ниже:
C="docker compose -f docker-compose.yml -f docker-compose.prod.yml"
Обычная выкладка
-
Изменения попадают в
mainтолько через PR в Gitea; CI (.gitea/workflows/ci.yml) должен быть зелёным. -
На сервере:
ssh lux-prod ~/lux_fiscal/scripts/deploy.sh
deploy.sh делает: бэкап БД (scripts/backup.sh) → git fetch + fast-forward main → up -d --build --wait
(миграции применяет одноразовый контейнер migrate) → проверка /api/v1/health через nginx фронтенда.
Если новая версия не поднялась — откатывает код на предыдущий коммит и печатает путь к бэкапу.
БД автоматически не откатывается. --force — пересобрать и перезапустить без новых коммитов.
После выкладки:
$C ps
$C logs --since 10m api worker | grep -iE "error|exception" | tail -20
Правила, чтобы не сломать прод
- Миграции — только совместимые с данными. Новые колонки —
nullableили сserver_default. Удаление/переименование колонки — отдельным релизом, после того как код перестал её использовать (иначе откат кода не спасёт). - Перед мержем миграцию прогоняют локально на копии свежего прод-бэкапа (см. «Прод-данные локально»).
ENCRYPTION_KEYв прод-.envникогда не меняется: им зашифрованы ключи касс в БД. Копия.envхранится отдельно от сервера (менеджер паролей).- В проде запрещены
CHECKBOX_USE_STUB=trueиCRM_USE_STUB=true— приложение упадёт на старте клиента. - Руками на сервере файлы репозитория не правятся:
deploy.shоткажется работать с грязным деревом. - Выкладка — не в часы пик работы кассиров: во время
upAPI недоступен несколько секунд, а воркер перезапускается.
Откат
Код:
cd ~/lux_fiscal
git log --oneline -10
git checkout --detach <коммит>
$C up -d --build --wait
Следующий deploy.sh сам вернётся на main. Правильный путь исправления — revert-коммит через PR.
БД из бэкапа (всё, что было после бэкапа, потеряется):
cd ~/lux_fiscal
$C stop api worker
$C exec -T postgres sh -c 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists --no-owner' \
< ~/backups/lux_fiscal/<файл>.dump
$C start api worker
Код при этом должен соответствовать версии схемы в дампе (alembic_version).
Бэкапы
-
scripts/backup.sh:pg_dump -Fc→ проверкаpg_restore --list→ ротация 14 дней. -
Cron пользователя
deploy(crontab -l): каждый день в 03:30 UTC. -
Бэкапы лежат на том же диске, что и БД, — периодически копируйте их за пределы сервера:
scp "lux-prod:backups/lux_fiscal/*.dump" D:/Backups/lux_fiscal/
Прод-данные локально
Прод-дамп содержит зашифрованные ключи касс, а локальный .env — ключи CRM. Чтобы локальный воркер не создавал боевые
чеки и не менял статусы заказов в CRM, локально всегда:
ENVIRONMENT=local
CHECKBOX_USE_STUB=true
CRM_USE_STUB=true
После восстановления прод-дампа в локальную БД сразу обнулите ключи касс (лицензия/PIN — фиктивные, ключ НП — NULL):
docker compose run --rm --no-deps -T api python - <<'EOF'
import asyncio
from sqlalchemy import text
from app.core import crypto
from app.core.config import settings
from app.db.session import engine
assert not settings.is_production
async def main():
async with engine.begin() as c:
await c.execute(text("update cash_registers set license_key_enc=:l, cashier_pin_enc=:p, np_api_key_enc=null"),
{"l": crypto.encrypt("local-stub-license"), "p": crypto.encrypt("0000")})
asyncio.run(main())
EOF
Зашифрованы только ключи касс, и скрипт их перезаписывает — поэтому локальный ENCRYPTION_KEY может быть любым.
Делайте это до первого запуска локальных api/worker.
Варнинги воркера ettn_poll_failed … не знайдено на прод-копии — норма: стаб Checkbox не знает ID настоящих чеков.
CI (Gitea Actions)
.gitea/workflows/ci.yml: backend — ruff check + pytest; frontend — npm run lint + npm run build.
Workflow выполняется только при зарегистрированном act_runner:
- Gitea → Site Administration → Actions → Runners → Create new Runner — скопировать registration token.
- Запустить раннер контейнером рядом с Gitea (образ
gitea/act_runner, переменныеGITEA_INSTANCE_URL,GITEA_RUNNER_REGISTRATION_TOKEN, проброс/var/run/docker.sock).
Сервер слабый (2.5 ГБ RAM): при выкладке во время прогона CI сборка фронтенда может упереться в память.
Первичная установка (для справки)
- Пользователь
deployв группеdocker, SSH-ключ разработчика в~deploy/.ssh/authorized_keys(700/600, владелецdeploy). - Deploy key
~deploy/.ssh/gitea_deployдобавлен в Gitea (repo → Settings → Deploy Keys, только чтение);git clone gitea:lauadmin/lux_fiscal.git ~/lux_fiscal. .envиз.env.example:ENVIRONMENT=production,DEBUG=false,BASE_URL/CORS_ORIGINS=https://asist.ystyle.com.ua, сгенерированныеSECRET_KEY/POSTGRES_PASSWORD,ENCRYPTION_KEY— тот, которым зашифрованы ключи касс в переносимой БД.$C up -d --build --wait; на пустой БД —$C run --rm api python -m app.cli bootstrap.- NPM: Proxy Host →
lux-fiscal-frontend:80, Let's Encrypt, Force SSL. - Cron:
30 3 * * * $HOME/lux_fiscal/scripts/backup.sh >> $HOME/backups/lux_fiscal/backup.log 2>&1.